之前有補充過二叉搜索樹的相關內容,傳送門。我們知道一棵高度為h的二叉搜索樹,它可以支持任何一種基本動態集合操作,其時間復雜度均為O(h)。因此,如果搜索樹的高度較高時,這些集合操作可能並不比鏈表上執行得快。紅黑樹(red-black tree)是許多“平衡”搜索樹的一種,可以保證最壞情況下基本動態集合操作的時間復雜度為O(lgn)。
紅黑樹是一棵二叉搜索樹,它在每個結點上增加了一個存儲位來表示結點的顏色,可以是Red或者Black。通過對任何一條從根到葉子的簡單路徑上各個結點的顏色進行約束,紅黑樹確保沒有一條路徑會比其他路徑長出2倍,因而是近似平衡的。
紅黑樹種的每個結點包含5個屬性:color,key,left,right和p。如果一個結點沒有子結點或父結點,則該結點相應指針屬性的值為Null,我們把這些Null視為指向二叉搜索樹的葉結點(外部結點)的指針,而把帶關鍵字的結點視為樹的內部結點。一棵紅黑樹是滿足下面紅黑性質的二叉搜索樹:
- 每個結點要么是紅色的,要么是黑色的。
- 根結點是黑色的。
- 每個葉結點是黑色的。
- 如果一個結點是紅色的,則它的兩個子結點都是黑色的。
- 對每個結點,從該結點到其所有后代葉結點的簡單路徑上,均包含相同數目的黑色結點。
從某個結點x出發(不含該結點)到達一個葉結點的任意一條簡單路徑上黑色結點的個數稱為該結點的黑高。記為bh(x),根據上面的性質5,黑高的概念定義是明確的,因為從該結點出發的所有下降到其葉結點的簡單路徑的黑結點個數都相同。於是定義紅黑樹的黑高為其根結點的黑高。
一棵有n個內部結點的紅黑樹的高度至少為2lg(n+1)
旋轉
搜索樹操作TREE-INSERT和TREE-DELETE在含n個關鍵字的紅黑樹上,運行花費時間為O(lgn)。由於這個兩操作對樹做了修改,結果可能違反紅黑樹的性質。為了維護這種性質,必須要改變樹種某些結點的顏色以及指針結構。其中指針結構的修改是通過旋轉來完成的,這是一種能保持二叉搜索樹性質的搜索樹局部操作。

如上圖所示給出的兩種旋轉方式所示:當在某個結點x上做左旋時,假設它的右孩子為y而不是null;x可以是右孩子不是null的樹T內的任意結點。左旋以x到y的鏈為“軸”進行。它使y成為該子樹新的根結點,x成為y的左孩子,y的左孩子成為x的右孩子。
同樣是以左旋操作舉例,用筆者的理解來解釋是: 所謂左旋,按我們常規理解來說其實是一個逆時針方向轉動的過程。即結點逆時針調整位置的過程。因此,原來子樹的根結點x應當成為新的根結點的左孩子,原來的子樹的根結點x的右孩子由於逆時針轉動,成為了新的子樹的根結點。但這里有一個需要額外考慮的地方,即x本身是有右孩子的,並且y本身是有左孩子的(可能為null)。經過上面的選擇過程,x原本的右孩子y成為了根幾點,不再是x的右孩子。而y本身的左孩子β則沒有了父結點。其實這時應該把β的父節點指向x,把x的右孩子指向β。
明白了思路接下來我們來看一下偽代碼實現
1 LEFT-ROTATE(T,x) 2 { 3 y = x.right; 4 x.right = y.left; 5 6 if(y.left != null) 7 { 8 y.left.p = x; 9 } 10 11 if(x.p == null) 12 { 13 T.root = y; 14 } 15 else if(x == x.p.left) 16 { 17 x.p.left = y; 18 } 19 else 20 { 21 x.p.right = y; 22 } 23 24 y.left = x; 25 x.p = y; 26 }
左旋和右旋操作都在O(1)的時間內完成。在旋轉操作中只有指針改變,其他所有屬性保持不變。
待續。。。
