算法導論——紅黑樹

  紅黑樹是一棵二叉搜索樹,每一個結點上增長了一個屬性來存儲顏色是紅色仍是黑色,紅黑樹能夠確保沒有一條路徑會比其餘路徑長出2倍,因此近似能夠認爲是平衡的。spa

  每一個結點包含5個屬性:color, key, left, right, p。若是一個結點沒有子結點或者父結點,則該結點的相應指針屬性爲NIL,這些NIL能夠視爲樹的葉結點,帶關鍵字的結點視爲內部結點。3d

紅黑樹具備如下性質:指針

  1. 每一個結點都是紅色的或者是黑色的
  2. 根結點是黑色的
  3. 每一個葉結點NIL是黑色的
  4. 若是一個結點是紅色的,它的兩個子結點都是黑色的
  5. 每一個結點到其餘全部後代葉結點的簡單路徑上,均包含相同數目的黑色結點,這個屬性被稱爲黑高,記做bh(x)

如圖是一個紅黑樹的例子,每一個結點邊上的數字表示其黑高。每一個NIL葉結點都是黑色的。可使用T.nil哨兵來表示全部的葉結點和根結點的父結點來簡化存儲,以下圖code

由於一般咱們不須要去過多研究NIL結點,因此能夠直接省略,集中研究內部結點blog

紅黑樹的黑高定義爲根結點的黑高。一棵有n個內部結點的紅黑樹的高度至多爲2lg(n+1),所以SEARCH、MINIMUM、MAXIMUM、SUCCESSOR、PREDECESSOR的操做時間爲O(lgn)class

旋轉

因爲TREE-INSERT和TREE-DELETE操做可能會破壞紅黑樹的性質,所以在這以後須要改變某些結點的顏色和指針結構。修改指針結構的操做就是旋轉二叉樹

 

旋轉分爲左旋和右旋,以左旋爲例,y本來是x的右兒子,左旋以後y成爲x父親的新兒子,x成爲y的左兒子,y的左兒子成爲x的右兒子,也就是從上圖的右邊變爲左邊。旋轉操做都在O(1)時間內完成,過程當中出了指針外其餘屬性不變。下圖展現了用左旋修改一棵樹搜索

 1 LEFT-ROTATE(T,x)
 2     y=x.right//y是x的左兒子
 3     x.right=y.left//y的左兒子成爲x的右兒子
 4     if y.left != T.nil
 5         y.left.p=x
 6     y.p=x.p
 7     if x.p == T.nil
 8         T.root=y//若x爲根則旋轉後y爲根
 9     else if x==x.p.left//不然y成爲x父結點的新兒子
10         x.p.left=y
11     else
12         x.p.right=y
13     y.left=x//x成爲y的左兒子
14     x.p=y
15 
16 RIGHT-ROTATE(T.y)
17     x=y.left//x是y的左兒子
18     y.left=x.right;//x的右兒子成爲x的左兒子
19     if x.right != T.nil
20         x.right.p=y
21     x.p=y.p
22     if y.p == T.nil
23         T.root=x//若y爲根則旋轉後x爲根
24     else if y==y.p.left//不然x成爲y父結點的新兒子
25         y.p.left=x
26     else
27         y.p.right=x
28     x.right=y//y成爲x的右兒子
29     y.p=x

插入

經過RB-INSERT將一個結點像普通二叉樹那樣插入紅黑樹並設爲紅色,而後使用INSERT-FIXUP來對節點從新着色並旋轉循環

 1 RB-INSERT(T,z)
 2     y=T.nil
 3     x=T.root
 4     while x!=T.nil
 5         y=x
 6         if z.key<x.key
 7             x=x.left
 8         else
 9             x=x.right
10     z.p=y
11     if y==T.nil
12         T.root=z
13     else if z.key<y.key
14         y.left=z
15     else
16         y.right=z
17     z.left=T.nil
18     z.right=T.nil
19     z.color=RED
20     RB-INSERT-FIXUP(T,z)
21 
22 RB-INSERT-FIXUP(T,z)
23     while z.p.color==RED
24         if z.p==z.p.p.left//若z的父親是左兒子
25             y=z.p.p.right//y是z的叔叔
26             if y.color==RED//狀況1
27                 z.p.color=BLACK
28                 y.color=BLACK
29                 z.p.p.color=RED
30                 z=z.p.p
31             else if z==z.p.right//狀況2
32                 z=z.p
33                 LEFT-ROTATE(T,z)
34             z.p.color=BLACK//狀況3
35             z.p.p.color=RED
36             RIGHT-ROTATE(T,z.p.p)
37         else//left和right交換其餘同上面狀況
38             y=z.p.p.left//y是z的叔叔
39             if y.color==RED//狀況1
40                 z.p.color=BLACK
41                 y.color=BLACK
42                 z.p.p.color=RED
43                 z=z.p.p
44             else if z==z.p.left//狀況2
45                 z=z.p
46                 RIGHT-ROTATE(T,z)
47             z.p.color=BLACK//狀況3
48             z.p.p.color=RED
49             LEFT-ROTATE(T,z.p.p)
50         T.root.color=BLACK

先分析一下可能會形成紅黑樹性質被破壞的狀況:方法

狀況1:z的叔結點y是紅色的,這種狀況下不管z是左兒子仍是右兒子均可以只靠染色來維持紅黑樹性質。

狀況2:z的叔結點y是黑色的且z是一個右孩子

狀況3:z的叔結點y是黑色的且z是一個左孩子

狀況2能夠經過左旋轉變到狀況3,狀況3再經過改變某些結點的顏色後進行右旋,保持紅黑樹的性質

下面是一個完整的操做實例

如圖z和z的父結點z.p都是紅色,且z.p有右兄弟y爲紅色,屬於狀況1,將z.p和y都染黑而後z.p.p染紅,z指向z.p.p。如今z.p的右兄弟y是黑色的,同時z是右兒子屬於狀況2,z指向z.p後對z進行左旋。如今z.p有右兄弟y是黑色的,z是左兒子屬於狀況3,z.p染黑.z.p.p染紅而後對z.p.p進行右旋,最後確保根結點是黑色的。

刪除

  紅黑樹的結點刪除操做一樣是基於二叉樹刪除改造而來,首先是TRANSPLANT方法,這個方法的做用是要刪除結點u時把結點v填到u本來的位置。而後就是實際刪除操做RB-DELETE, z的子結點少於2個時,刪除z結點,子結點取代z的位置。z有兩個兒子時,y是右子樹中最小的一個做爲z的後繼,y移動到z的位置,z的左子樹移交給y,y.right替換y本來的位置,若y本來是黑色,則須要檢查y.right是否破壞了紅黑樹結構。因爲刪除後可能會破壞紅黑樹性質,因此和插入同樣也須要執行修復操做

 1 RE-TRANSPLANT(T,u,v)
 2     if u.p==T.nil
 3         T.root=v
 4     else if u==u.p.left
 5         u.p.left=v
 6     else
 7         u.p.right=v
 8     v.p=u.p//v.p的賦值無條件執行,由於u是root時v.p是T.nil該賦值依然成立
 9 
10 RB-DELETE(T,z)
11     y=z
12     y-original-color=y.color
13     if z.left=T.nil
14         x=z.right
15         RB-TRANSPLANT(T,z,z.right)//z左兒子不存在則右兒子頂替z
16     else if z.right==T.nil
17         x=z.left
18         RB-TRANSPLANT(T,z,z.left)// z右兒子不存在則左兒子頂替z
19     else
20         y=TREE-MINIMUM(z.right)//y是z的右子樹中最小的結點,即除NIL以外最左的結點
21         x=y.right//x是y的右兒子
22         if y.p==z
23             x.p=y//若y的右子樹中沒有左兒子,x.p=y(原本就是)
24         else
25             RB-TRANSPLANT(T,y,y.right)//用y.right替換y的位置
26             y.right.p=y
27         RB-TRANSPLANT(T,z,y)//用y替換z的位置
28         y.left=z.left//z的左子樹移到y的左子樹上,y自己沒有左子樹
29         y.left.p=y
30         y.color=z.color//y的顏色換成z的顏色
31     if y-original-color==BLACK
32         RE-DELETE-FIXUP(T,x)//若y本來是黑色則移走y後本來包含y路徑的黑高會改變致使破壞紅黑樹性質
33 
34 RB-DELETE-FIXUP(T,x)
35     while x != T.root and x.color == BLACK
36         if x == x.p.left
37             w=x.p.right//w是x的兄弟
38             if w.color == RED
39                 w.color=BLACK//case1
40                 x.p.color=RED
41                 LEFT-ROTATE(T,x,p)
42                 w=x.p.right
43             if w.left.color == BLACK and w.right.color == BLACK
44                 w.color=RED//case2
45                 x=x.p
46             else if w.right.color == BLACK
47                 w.left.color=BLACK//case3
48                 w.color=RED
49                 RIGHT-ROTATE(T,w)
50                 w=x.p.right
51             w.color=x.p.color//case4
52             x.p.color=BLACK
53             w.right.color=BLACK
54             LEFT-ROTATE(T,x,p)
55             x=T.root
56         else(上面的狀況交換left right)
57     x.color=BLACK

狀況1:x的兄弟w是紅色的,w必定會有黑色的子結點,改變w和x.p的顏色,而後對x.p進行一次左旋。如今w是本來w的某個子結點,可能轉爲狀況234

狀況2:x的兄弟w是黑色的,w的兩個子結點都是黑色的。w染紅,x上升到x.p,若本來x.p是紅色的(從狀況1進入狀況2就是這樣的)則循環結束,將新的x染黑便可。

狀況3:x的兄弟w是黑色的,w左兒子紅色,右兒子黑色。交換w和w.left的顏色,而後對w進行右旋,這樣w有一個紅色的右兒子,轉入狀況4

狀況4:x的兄弟結點w是黑色的,且w的右兒子是紅色的,左兒子顏色不限。對x.p執行左旋以後,再對部分結點從新染色。

相關文章
相關標籤/搜索