算法學習之數據結構之紅黑樹(一)

  一,紅黑樹性質。

  由於二叉查找樹知道,一個高度爲h的二叉查找樹可以實現任何一種基本的動態幾何操作,如search,insert,minimum,delete,successor等操作,其時間都是O(h),這樣樹的高度低了就會執行的比較快,但是當樹的高度較高時,操作的性能可能不比鏈表好。紅黑樹是許多“平衡的”查找樹中的一種,他能保證在最壞的情況下,基本的動態集合操作的時間爲O(lgn)。

  紅黑樹的性質。紅黑樹是一種二叉查找樹,,每一個節點增加一個存儲位表示節點的顏色,爲紅色或者黑色。紅黑樹確保沒有一條路徑會比其他路徑長出兩倍。因而是接近平衡的。
  樹的每個節點包含五個域:color, parent, key, left, right。一個二查找樹如果滿足如下紅黑性質,則爲一個紅黑樹。
  1)每個節點或是紅的,或是黑的。  
  2)根節點是黑。
  3)每個葉節點(NIL)是黑的。
  4)如果一個節點是紅的, 則它的兩個孩子都是黑的。
  5)對每個節點,從該節點到其子孫節點的所有路徑上包含相同數目的黑節點。
  從某個節點x出發到達一個葉節點的任意一條路徑上,黑色節點的個數成爲該節點x的黑高度。
  一個有n個內節點的紅黑樹的高度至多爲2lg(n+1)
  

  二、紅黑樹旋轉。

  當對紅黑樹上進行insert和delete時,對樹進行了修改,結果可能會違反紅黑樹的性質,所以需要改變樹中的節點的顏色和指針結構,即需要進行旋轉。
  左旋,假設它的右孩子節點y不是nil[T];x可以爲樹內任意右孩子不是nil[T]的節點,左旋以x和y之間的鏈爲軸進行,它使y成爲該子樹新的根,x成爲y的左孩子節點,而y的左孩子節點爲x的右孩子。
  僞代碼如下:
left-rotate(T, x)
y = right[x]
right[x] = left[y] // y的左子樹變爲x的右子樹
parent[left[y]] = x
parent[y] = parent[x] // x的父親節點是y的父親節點
if parent[y] == null
    then root[T] = y // x的根節點是根節點則置爲y
    else if x == left[parent[x]] // 如果x是其父節點的左孩子節點,則將y置爲x父節點的左孩子節點否則置爲其父節點的右孩子節點
             then left[parent[x]] = y
             else right[parent[x]] = y
left[y] = x // y的左孩子結點是x
parent[x] = y // x的父親節點是y

  三、紅黑樹插入。

  
  分爲兩部分,先把節點插入紅黑樹中,並着爲紅色,然後進行修補以保持紅黑樹的性質。先看前一部分的僞代碼,和二叉查找樹的插入大部分類似。

rb-insert(T, z)
y = null
x = root[T]
while x != null
    do y = x
        if(key[x] < key[z])
            then x = right[x]
            else x = left[x]
parent[z] = y
if(y == null) //樹是空的
    then root[T] = z
    else if key[z] < key[y]
        then left[y] = z
        else right[y] = z
left[z] = nil[T]
right[z] = nil[T]
color[z] = RED // 着色爲紅色
rb-insert-fixup(T, z)

  就最後四行和二叉查找樹不一樣,其他的都一樣,並着爲紅色,然後進行修補以保持紅黑樹的性質,接下來看rb-insert-fixup(T, z),將插入的節點z着爲紅色以後,可能破壞的性質有性質2和性質4,所以我們要做的就是恢復性質2和性質4.先大致說一下判斷情況:
1,首先讓一個指針y指向z的叔叔節點(z是要刪除的節點)。
2,如果y是紅色的,CASE1。則使z的父親節點和叔叔節點改爲黑色,z的祖父及誒單改爲紅色。然後z轉移到z的祖父節點。
3,當y的顏色是黑色時,
①.首先判斷z是否是其父親結點的右兒子,若是,則調整爲左兒子(用旋轉)。 CASE2。
②.其次使z的父親結點顏色爲黑,z的祖父結點顏色爲紅,並以z的祖父結點爲軸右旋,CASE3。看僞代碼吧。
僞代碼如下:

rb-insert-fixup(T, z)
while color[p[z]] = RED
    do if parent[z] == left[parent[parent[z]]] // z節點的父親節點是其祖父節點的左孩子節點
             then y = right[parent[parent[z]]] // y爲z節點的祖父節點的右孩子節點,即叔叔節點
                    if color[y] == RED // 叔叔節點爲紅色 CASE1
                        then color[parent[z]] = black // 父親節點黑色
                               color [y] = black // 叔叔節點黑色
                               color[parent[parent[z]] = red // 祖父節點紅色
                               z = parent[parent[z]]
                        esle if z == right[parent[z]] // 叔叔節點是黑的,且z爲父節點的右孩子節點 CASE2
                                 then z = parent[z]
                                        left-rotate(T, z)  // 左旋轉
                         // CASE3
                        color[parent[z]] = black //把父節點着黑色
                        color[parent[parent[z]]] = red //原來的祖節點着紅色
                        right-rotate(T, z)
         else // z節點的父親節點是其祖父節點的右孩子節點
                操作和左孩子節點一樣
color[root[T]] = black // 最後把根節點置爲黑色

具體解釋如下:

  根據Z的父節點是Z的祖節點的左子節點還是右子節點,分爲兩組對稱的情況,每組有3種情況。以Z的父節點是Z祖節點的左子節點爲例:  

RBT-Insert3

第一種:Z的“叔父”節點是紅色。

 

             RBTree1 

 

如上圖所示,在這種情況下,將父、叔節點都着爲黑色,再將子樹根節點着爲紅色,那麼子樹的黑高度沒有發生改變,而且紅黑性質得得到了調整。此時,再將Z指向子樹的根節點,向上遞歸恢復紅黑特性。

第二種:Z的“叔父”節點是黑色的,Z的父節點的右子節點。 

 

如上圖,將Z本身與其父節點進行一次左旋,讓Z指向原來的父節點,就可以調整爲情況三,下面看情況三。

第三種:Z的“叔父”節點是黑色的,Z的父節點的左子節點。
 

如上圖,將Z的父節點與祖節點進行一次右旋,並把父節點着黑色,原來的祖節點着紅色。這些子樹的紅黑特性得到了恢復,而且子樹的黑高度沒有變化。另外,由於子樹根節點已經是黑色了(這個節點不會出現父子同爲紅色的問題了),所以不必再向上遞歸了,此時整個樹的紅黑特性都已經是正確的了。

由於紅黑樹的刪除比較複雜,放在下一篇。

網上發現不錯的學習資料,分享一下:黑樹算法的層層剖析與逐步實現


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