今天咱們來學一下數據結構方面的知識,對紮實 Java 的基本功很是有用,學會了就會有一種自帶大佬的感受,嘿嘿。數據結構,也就是 Data Structure,是一種存儲數據的結構體,數據與數據之間存在着必定的關係,這樣的關係有數據的邏輯關係、數據的存儲關係和數據的運算關係,整理一份MySQL學習筆記,數據結構和MySQL仍是離不開的。在 Java 中,數據結構通常能夠分爲兩大類:線性數據結構和非線性數據結構。哈哈,這個名字頗有靈魂吧?面試
一眼看上去就知道的,像 String []、int [] 這種;還有須要看兩眼才能看透的(看源碼了),像 ArrayList,內部對數組進行了封裝。算法
數組這種數據結構最大的好處,就是能夠根據下標(或者叫索引)進行操做,插入的時候能夠根據下標直接插入到具體的位置,但與此同時,後面的元素就須要所有向後移動,須要移動的數據越多,就越累。數據庫
假設如今已經有了一個 ArrayList 了,準備在第 4 個位置(下標爲 3)上添加一個元素 55。數組
此時 ArrayList 中國 5 個位置之後的元素將會向後移動。數據結構
準備把 23 從 ArrayList 中移除。ide
此時下標爲 七、八、9 的元素往前挪。學習
簡單總結一下 ArrayList 的時間複雜度,方便你們在學習的時候做爲參考。優化
一、經過下標(也就是 get(int index))訪問一個元素的時間複雜度爲 O(1),由於是直達的,不管數據增大多少倍,耗時都不變。動畫
二、添加一個元素(也就是 add())的時間複雜度爲 O(1),由於直接添加到末尾。3d
三、刪除一個元素的時間複雜度爲 O(n),由於要遍歷列表,數據量增大幾倍,耗時也增大幾倍。
四、查找一個未排序的列表時間複雜度爲 O(n),由於要遍歷列表;查找排序過的列表時間複雜度爲 O(log n),由於可使用二分查找法,當數據增大 n 倍時,耗時增大 logn 倍(這裏的 log 是以 2 爲底的,每找一次排除一半的可能)。
鏈表在物理存儲空間是不連續的,但每一個節點要麼知道它的下一個節點是誰,要麼知道它的上一個節點是誰,彷彿就像咱們之間隔着千山萬水,卻心有靈犀一點鏈。像 LinkedList 就是最典型的鏈表結構,經過引用相互連接。
LinkedList 中的每個元素均可以稱之爲節點(Node),每個節點都包含三個項目:其一是元素自己,其二是指向下一個元素的引用地址,其三是指向上一個元素的引用地址。
LinkedList 看起來就像下面這個樣子:
相比 ArrayList,LinkedList 有如下優點:
一、LinkedList 容許內存進行動態分配,這就意味着內存分配是由編譯器在運行時完成的,咱們無需在 LinkedList 聲明的時候指定大小。
二、LinkedList 不須要在連續的位置上存儲元素,由於節點能夠經過引用指定下一個節點或者前一個節點。也就是說,LinkedList 在插入和刪除元素的時候代價很低,由於不須要移動其餘元素,只須要更新前一個節點和後一個節點的引用地址便可。
棧是一種很是有用的數據結構,它就像一摞盤子,第一個放在最下面,第二個放在第一個上面,第三個放在第二個上面,最後一個放在最上面。棧遵循後進先出的原則,也就是「Last In First Out」(簡稱 LIFO)——最後的一個進的,最早出去。
對於棧這樣一個數據結構來講,它有兩個常見的動做:
隊列,只容許在隊尾添加數據,隊首移除數據。隊列在 Java 中的出現頻率很是高,有各類不一樣的類來知足不一樣的場景需求。像優先級隊列 PriorityQueue、延時隊列 DelayQueue 等等。隊列遵循的是 First In First Out,縮寫爲 FIFO,也就是先進先出,第一個進入隊列的第一個先出來。
樹是一種典型的非線性結構,它是由 n(n>0)個有限節點組成的一個具備層次關係的集合。之因此叫「樹」,是由於這種數據結構看起來就像是一個倒掛的樹,只不過根在上,葉在下。樹形數據結構有如下這些特色:
下圖展現了樹的一些術語:
根節點是第 0 層,它的子節點是第 1 層,子節點的子節點爲第 2 層,以此類推。
對子節點沒有任何約束。
每一個節點最多含有兩個子節點的樹。 二叉樹按照不一樣的表現形式又能夠分爲多種。
每一個子節點的父節點不必定有兩個子節點的二叉樹。
對於一棵二叉樹,假設其深度爲d(d>1)。除了第 d 層外,其它各層的節點數目均已達最大值,且第 d 層全部節點從左向右連續地緊密排列。
一棵每一層的節點數都達到了最大值的二叉樹。有兩種表現形式,第一種,像下圖這樣(每一層都是滿的),知足每一層的節點數都達到了最大值 2。
英文名叫 Binary Search Tree,即 BST,須要知足如下條件:
當且僅當任何節點的兩棵子樹的高度差不大於 1 的二叉樹。由前蘇聯的數學家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉樹,根據科學家的英文名也稱爲 AVL 樹。
平衡二叉樹本質上也是一棵二叉查找樹,不過爲了限制左右子樹的高度差,避免出現傾斜樹等偏向於線性結構演化的狀況,因此對二叉搜索樹中每一個節點的左右子樹做了限制,左右子樹的高度差稱之爲平衡因子,樹中每一個節點的平衡因子絕對值不大於 1。
平衡二叉樹的難點在於,當刪除或者增長節點的狀況下,如何經過左旋或者右旋的方式來保持左右平衡。
紅黑樹是一種常見的平衡二叉樹,節點是紅色或者黑色,經過顏色的約束來維持着二叉樹的平衡:
一種對讀寫操做進行優化的自平衡的二叉查找樹,可以保持數據有序,擁有多於兩個的子樹。
B 樹的變體
HashMap 裏面的 TreeNode 就用到了紅黑樹,而 B 樹、B+ 樹在數據庫的索引原理裏面有典型的應用。
哈希表(Hash Table),也叫散列表,是一種能夠經過關鍵碼值(key-value)直接訪問的數據結構,它最大的特色就是能夠快速實現查找、插入和刪除。其中用到的算法叫作哈希,就是把任意長度的輸入,變換成固定長度的輸出,該輸出就是哈希值。像 MD五、SHA1 都用的是哈希算法。
每個 Java 對象都會有一個哈希值,默認狀況就是經過調用本地方法執行哈希算法,計算出對象的內存地址 + 對象的值的關鍵碼值。
數組的最大特色就是查找容易,插入和刪除困難;而鏈表正好相反,查找困難,而插入和刪除容易。哈希表很完美地結合了二者的優勢, Java 的 HashMap 在此基礎上還加入了樹的優勢。
哈希表具備較快(常量級)的查詢速度,以及相對較快的增刪速度,因此很適合在海量數據的環境中使用。
對於任意兩個不一樣的數據塊,其哈希值相同的可能性極小,也就是說,對於一個給定的數據塊,找到和它哈希值相同的數據塊極爲困難。再者,對於一個數據塊,哪怕只改動它的一個比特位,其哈希值的改動也會很是的大——這正是 Hash 存在的價值!
儘管可能性極小,但仍然會發生,若是哈希衝突了,Java 的 HashMap 會在數組的同一個位置上增長鏈表,若是鏈表的長度大於 8,將會轉化成紅黑樹進行處理——這就是所謂的拉鍊法(數組+鏈表)。
圖是一種複雜的非線性結構,由頂點的有窮非空集合和頂點之間邊的集合組成,一般表示爲:G(V,E),其中,G 表示一個圖,V 視圖 G 中頂點的集合,E 是圖 G 中邊的集合。
上圖共有 V0,V1,V2,V3 這 4 個頂點,4 個頂點之間共有 5 條邊。
在線性結構中,數據元素之間知足惟一的線性關係,每一個數據元素(除第一個和最後一個外)均有惟一的「前驅」和「後繼」;
在樹形結構中,數據元素之間有着明顯的層次關係,而且每一個數據元素只與上一層中的一個元素(父節點)及下一層的多個元素(子節點)相關;今日讀者福利:學習筆記+面試真題。
而在圖形結構中,節點之間的關係是任意的,圖中任意兩個數據元素之間都有可能相關。