數據結構與Java

紅黑樹

  • 左旋右旋速記口令:
    父與左子爲右旋,
    旋完子右變父左,
    其他都不變。
    父與右子爲左旋,
    旋完子左變父右,
    其他都不變。
  • 紅黑樹之插入
    將新插入結點染爲紅色
    WHILE若新插入點不爲null,不是根結點,並且其父結點顏色爲紅色
    IF若當前結點的父結點是當前結點的爺爺結點的左子結點
    IF若當前結點的叔叔結點爲紅
    將當前結點的父結點染爲黑
    將叔叔結點染爲黑
    將爺爺結點染爲紅
    將爺爺結點置爲當前結點
    ELSE若當前結點的叔叔結點顏色爲黑或NULL
    若當前結點爲右子結點
    將父結點置爲當前結點
    左旋當前結點
    將當前結點的父結點染爲黑
    將當前結點的爺爺結點染爲紅
    右旋當前結點的爺爺結點
    ELSE對稱…
    將根結點染爲黑
  • 紅黑樹之刪除(按照Java中TreeMap的邏輯進行書寫,順序爲由上至下):
    若要刪除的結點左右子結點均存在:
    尋找右子結點往下的最後的左子結點,
    以該結點作爲後繼結點(即下文中的當前結點)。
    若要刪除的結點只存在唯一結點,
    則以唯一結點作爲後繼結點。
    若要刪除的結點爲黑色,則需對該樹進行修復操作。
    修復:
    WHILE若當前結點爲黑色,且不是根結點:
    IF若當前結點爲左子結點:
    若當前結點的兄弟結點爲紅色
    將兄弟結點染爲黑色,將父結點染爲紅色,左旋父結點。
    IF若當前節點的兄弟結點有左右兩個子結點,並且均爲黑色
    將兄弟結點染爲紅色,將當前結點的父結點置爲當前結點。
    ELSE若當前結點的兄弟結點只有右子結點爲黑
    將兄弟結點的左子結點染爲黑色,將兄弟結點染爲紅色,右旋兄弟結點。
    將當前兄弟結點染爲父節點的顏色,將父節點染爲黑色,將兄弟結點的右子結點染爲黑色,
    左旋父節點,將根節點賦值給當前結點。
    ELSE對稱。。。
    將當前結點染爲黑色。

時間複雜度

常數階O(1)<對數階O(log2n)<線性階O(n)<線性對數階O(nlog2n)<平方階O(n2)`<`k次方階O(nk)
<指數階O(2n)`<`O(n!)`<`O(nn)

HashMap

從結構實現來講,HashMap是數組+鏈表+紅黑樹實現的,HashMap底層是由數組組成Node[],哈希桶數組,Node是HashMap的一個內部類,實現了Map.Entry接口,該類中有幾個重要字段:hash字段,key字段,value字段,next字段,在往HashMap中存儲鍵值對時,過程大概是這樣的:

  1. 判斷鍵值對數組table[i]是否爲空或爲null,否則執行resize()進行擴容
  2. 根據鍵key計算hash值,得到插入的數組索引i,如果table[i] == null,直接新建結點添加,轉向6,若果table[i]不爲空,轉向3;
  3. 判斷table[i]的首個元素是否和key一樣,如果相同直接覆蓋value,否則轉向4,這裏的相同指的是hashCode以及equals;
  4. 判斷table[i]是否爲treeNode,即table[i]是否是紅黑樹,如果是紅黑樹,則直接在樹種插入鍵值對,否則轉向5;
  5. 遍歷table[i],判斷鏈表長度是否大於8,大於8的話把鏈表轉換爲紅黑樹,在紅黑樹中執行插入操作,否則進行鏈表的插入操作;遍歷過程中若發現key已經存在直接覆蓋value即可;
  6. 插入成功後,判斷實際存在的鍵值對數量size是否超過了最大容量threshold,如果超過,進行擴容。
  • HashMap默認初始長度length爲16,Load factor爲負載因子(默認值是0.75),threshold是HashMap所能容納的最大數據了的Node個數。threshold = length * Load factor(默認爲12)。若實際存在的鍵值對數量超過了12,就會進行一次擴容,擴容大小爲原來的一倍,也就是擴大到了32.
  • 爲什麼初始長度會設置爲16呢?以及爲什麼哈希桶數組的長度爲什麼必須是2的次冪呢?(程序中有處理,若開發人員手動傳入的長度不爲2的次冪,也會將其轉換爲大於這個數,且離這個數最近的2的次冪)主要是爲了在取模和擴容時做優化,同時爲了減少衝突,HashMap定位哈希桶索引位置時,也加入了高位參與運算的過程,當開發人員存儲一個key,value的鍵值對進入HashMap時,首先會將key值轉爲二進制的形式,然後和length-1(默認值即15二進制爲1111)進行&運算,得到hash值假設爲5,創建對應的Node對象後存儲到數組索引爲5的地方。
  • HashMap進行擴容resize時,會伴隨着rehash的過程,而這個過程比較耗費性能,因此若能預知該HashMap需要存儲的數據量,最好給HashMap指定初始值。(指定初始值時需考慮其threshold的值).

ArrayList

  • ArrayList默認容量爲10,若容量滿時,會擴充爲原來的1.5倍,若擴充後不是整數,則取整數部分。因爲ArrayList每次擴容都會伴隨着深拷貝的發生,因此若能預知該ArrayList需要存儲的數據量,最好給ArrayList指定初始值

List

  • ArrayList 採用索引(隨機訪問方式)遍歷效率最高
  • LinkedList 採用for增強循環遍歷效率最高,採用索引遍歷效率最低,不能用!
  • Vector 採用索引(隨機訪問方式)遍歷效率最高,若容量滿時,容量大小會增加一倍
  • Stack(棧)繼承於Vector
  • 實際使用的時候, 若需批量插入數據,可以使用LinkedList, 若數據全部插入後,在另外一個地方要對數據進行遍歷, 可通過new ArrayList<>(linkedList) 將LinkedList的引用對象轉爲ArrayList之後進行遍歷
  • 在JDK8中,若是小數據量的插入LinkedList快,若是大數據量的插入ArrayList快
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章