JAVA集合:紅黑樹、TreeMap、TreeSet

1.紅黑樹的性質
2.紅黑樹各種操作複雜度
3.紅黑樹相比於BST和AVL樹有什麼優點?
4.紅黑樹相對於哈希表,在選擇使用的時候有什麼依據?
5.java集合TreeMap.
6.java集合TreeSet
7.如何擴展紅黑樹來獲得比某個結點小的元素有多少個?

1.紅黑樹的性質

(1)每個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每個葉子節點(NIL)是黑色。 [注意:這裏葉子節點,是指爲空(NIL或NULL)的葉子節點!]
(4)如果一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

注意:
(01) 特性(3)中的葉子節點,是隻爲空(NIL或null)的節點。
(02) 特性(5),確保沒有一條路徑會比其他路徑長出倆倍。因而,紅黑樹是相對是接近平衡的二叉樹。

紅黑樹示意圖如下:
在這裏插入圖片描述

2.紅黑樹各種操作複雜度

能保證在最壞情況下,基本的動態幾何操作的時間均爲O(logn)

3.紅黑樹相比於BST和AVL樹有什麼優點?

BST:二叉查找樹。(不平衡,有序數據變成BST可能是一條鏈。)
AVL:平衡二叉查找樹。

RB-Tree和AVL樹作爲BBST,其實現的算法時間複雜度相同,AVL作爲最先提出的BBST,貌似RB-tree實現的功能都可以用AVL樹是代替,那麼爲什麼還需要引入RB-Tree呢?
(1)紅黑樹不追求"完全平衡",即不像AVL那樣要求節點的 |balFact| <= 1,它只要求部分達到平衡,但是提出了爲節點增加顏色,紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,任何不平衡都會在三次旋轉之內解決,而AVL是嚴格平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多。

(2)就插入節點導致樹失衡的情況,AVL和RB-Tree都是最多兩次樹旋轉來實現復衡rebalance,旋轉的量級是O(1)
刪除節點導致失衡,AVL需要維護從被刪除節點到根節點root這條路徑上所有節點的平衡,旋轉的量級爲O(logN),而RB-Tree最多只需要旋轉3次實現復衡,只需O(1),所以說RB-Tree刪除節點的rebalance的效率更高,開銷更小!

(3)AVL的結構相較於RB-Tree更爲平衡,插入和刪除引起失衡,如2所述,RB-Tree復衡效率更高;當然,由於AVL高度平衡,因此AVL的Search效率更高啦。

(4)針對插入和刪除節點導致失衡後的rebalance操作,紅黑樹能夠提供一個比較"便宜"的解決方案,降低開銷,是對search,insert ,以及delete效率的折衷,總體來說,RB-Tree的統計性能高於AVL.

(5)故引入RB-Tree是功能、性能、空間開銷的折中結果。
(5.1)AVL更平衡,結構上更加直觀,時間效能針對讀取而言更高;維護稍慢,空間開銷較大。
(5.2)紅黑樹,讀取略遜於AVL,維護強於AVL,空間開銷與AVL類似,內容極多時略優於AVL,維護優於AVL。

基本上主要的幾種平衡樹看來,**紅黑樹有着良好的穩定性和完整的功能,性能表現也很不錯,**綜合實力強,在諸如STL的場景中需要穩定表現。

紅黑樹的查詢性能略微遜色於AVL樹,因爲其比AVL樹會稍微不平衡最多一層,也就是說紅黑樹的查詢性能只比相同內容的AVL樹最多多一次比較,但是,紅黑樹在插入和刪除上優於AVL樹,AVL樹每次插入刪除會進行大量的平衡度計算,而紅黑樹爲了維持紅黑性質所做的紅黑變換和旋轉的開銷,相較於AVL樹爲了維持平衡的開銷要小得。
總結:實際應用中,若搜索的次數遠遠大於插入和刪除,那麼選擇AVL,如果搜索,插入刪除次數幾乎差不多,應該選擇RB。

4.紅黑樹相對於哈希表,在選擇使用的時候有什麼依據?

權衡三個因素: 查找速度, 數據量, 內存使用,可擴展性。
  總體來說,hash查找速度會比map快,而且查找速度基本和數據量大小無關,屬於常數級別;而map的查找速度是log(n)級別。並不一定常數就比log(n) 小,hash還有hash函數的耗時,明白了吧,如果你考慮效率,特別是在元素達到一定數量級時,考慮考慮hash。但若你對內存使用特別嚴格, 希望程序儘可能少消耗內存,那麼一定要小心,hash可能會讓你陷入尷尬,特別是當你的hash對象特別多時,你就更無法控制了,而且 hash的構造速度較慢。

紅黑樹並不適應所有應用樹的領域。如果數據基本上是靜態的,那麼讓他們待在他們能夠插入,並且不影響平衡的地方會具有更好的性能。如果數據完全是靜態的,例如,做一個哈希表,性能可能會更好一些。

在實際的系統中,例如,需要使用動態規則的防火牆系統,使用紅黑樹而不是散列表被實踐證明具有更好的伸縮性。Linux內核在管理vm_area_struct時就是採用了紅黑樹來維護內存塊的。

紅黑樹通過擴展節點域可以在不改變時間複雜度的情況下得到結點的秩。

5.java集合TreeMap.

6.java集合TreeSet

7.如何擴展紅黑樹來獲得比某個結點小的元素有多少個?

這其實就是求節點元素的順序統計量,當然任意的順序統計量都可以需要在O(lgn)時間內確定。

在每個節點添加一個size域,表示以結點 x 爲根的子樹的結點樹的大小,則有

size[x] = size[[left[x]] + size [right[x]] + 1;

這時候紅黑樹就變成了一棵順序統計樹。

利用size域可以做兩件事:

1). 找到樹中第i小的結點;

OS-SELECT(x;,i)  
r = size[left[x]] + 1;  
if i == r  
     return x  
elseif i < r  
     return OS-SELECT(left[x], i)  
else return OS-SELECT(right[x],  i) 

思路:size[left[x]]表示在對x爲根的子樹進行中序遍歷時排在x之前的個數,遞歸調用的深度不會超過O(lgn);

2).確定某個結點之前有多少個結點,也就是我們要解決的問題;

OS-RANK(T,x)  
r = x.left.size + 1;  
y = x;  
while y != T.root  
         if y == y.p.right  
                 r = r + y.p.left.size +1  
         y = y.p  
return r  

思路:x的秩可以視爲在對樹的中序遍歷種,排在x之前的結點個數加上一。最壞情況下,OS-RANK運行時間與樹高成正比,所以爲O (lgn).

8.擴展數據結構有什麼步驟?

1).選擇基礎數據結構;

2).確定要在基礎數據結構種添加哪些信息;

3).驗證可用基礎數據結構上的基本修改操作來維護這些新添加的信息;

4).設計新的操作。

9.爲什麼一般hashtable的桶數會取一個素數

設有一個哈希函數
H( c ) = c % N;
當N取一個合數時,最簡單的例子是取2n,比如說取23=8,這時候
H( 11100(二進制) ) = H( 28 ) = 4
H( 10100(二進制) ) = H( 20 )= 4

這時候c的二進制第4位(從右向左數)就”失效”了,也就是說,無論第c的4位取什麼值,都會導致H( c )的值一樣.這時候c的第四位就根本不參與H( c )的運算,這樣H( c )就無法完整地反映c的特性,增大了導致衝突的機率.

取其他合數時,都會不同程度的導致c的某些位”失效”,從而在一些常見應用中導致衝突.
但是取質數,基本可以保證c的每一位都參與H( c )的運算,從而在常見應用中減小衝突機率..

(個人意見:有時候不取質數效率也不會太差…但是無疑取質數之比較保險的…)

發佈了20 篇原創文章 · 獲贊 2 · 訪問量 2609
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章