紅黑樹是一棵二叉搜索樹,每個結點上增加了一個屬性來存儲顏色是紅色還是黑色,紅黑樹可以確保沒有一條路徑會比其他路徑長出2倍,所以近似可以認為是平衡的。
每個結點包含5個屬性:color, key, left, right, p。如果一個結點沒有子結點或者父結點,則該結點的相應指針屬性為NIL,這些NIL可以視為樹的葉結點,帶關鍵字的結點視為內部結點。
紅黑樹具有以下性質:
- 每個結點都是紅色的或者是黑色的
- 根結點是黑色的
- 每個葉結點NIL是黑色的
- 如果一個結點是紅色的,它的兩個子結點都是黑色的
- 每個結點到其他所有后代葉結點的簡單路徑上,均包含相同數目的黑色結點,這個屬性被稱為黑高,記作bh(x)

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

因為通常我們不需要去過多研究NIL結點,所以可以直接省略,集中研究內部結點

紅黑樹的黑高定義為根結點的黑高。一棵有n個內部結點的紅黑樹的高度至多為2lg(n+1),因此SEARCH、MINIMUM、MAXIMUM、SUCCESSOR、PREDECESSOR的操作時間為O(lgn)
旋轉
由於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執行左旋之后,再對部分結點重新染色。
