1. 紅黑樹(RED-BLACK TREE)引言:
-------------------------------------
紅黑樹(RBT)可以說是binary-search tree的非嚴格的平衡版本。與之相應的是平衡二叉樹(Balanced Binary Tree)又稱之為AVL樹(因為是G.M. Adelson-Velsky 和 E.M. Landis在1962年發明的這棵樹)是binary-search tree的嚴格的平衡版本。
BST達到最平衡的狀態稱之為AVL。在AVL樹中任何節點的兩個兒子子樹的高度最大差別為一,查找、插入和刪除在平均和最壞情況下都是O(lg n)。但因為增加和刪除節點可能會破壞“平衡狀態”,所以大多數情況下需要通過多次樹旋轉來重新平衡這個樹。所以簡單地說,如果你的應用查找次數遠遠多於增刪操作,那么AVL是最好的,但是如果增刪次數和查找次數不相上下時,RBT因為相比AVL沒有過多的旋轉操作,效率要比AVL高。並且在是實際情況中,RBT的應用也更為廣泛。至少《intro to algo》這本書上主要講了RBT。
2. 紅黑樹幾個基本屬性:
------------------------
因為RBT是binary-search tree的非嚴格的平衡版本,所以紅黑樹繼承了BST的基本屬性:key值,和基本性質:對於tree中任意節點x,都有x.left.key<x.key<=x.right.key. 此外,BST的葉子節點在RBT不稱之為葉子節點,RBT的葉子節點為NIL,所以BST的ROOT節點的父親節點在RBT表示里不再為空,而是NIL或者稱之為TREE.sentinel. BST中的葉子節點也是一樣,他們也有孩子為NIL節點。接下來是RBT的獨有屬性:color,即每個樹節點都有自己的color,要么紅要么黑。
然后是RBT的五個基本性質:1)每個樹節點要么是紅色要么是黑色;2)ROOT節點必須是黑色;3)NIL節點為黑色;4)如果一個節點是紅色,那么它的孩子節點都是黑色(NIL節點除外),從每個葉子到根的所有路徑上不能有兩個連續的紅色節點;5)從任一節點到其每個葉子的所有路徑都包含相同數目的黑色節點。 如下圖所示:
3. 旋轉:
--------
因為在執行操作或者刪除操作以后,RBT的性質可能會被改變。所以為了維護RBT的基本性質[2],我們需要改變一些樹的節點的顏色,同時需要改變樹的結構。下圖展示了樹的旋轉動作:
旋轉動作共有兩種:left 和 right。left-rotation 就是逆時針旋轉,right-rotation是順時針旋轉。以left-roation為例,如上圖右側所示,
第一步:建立y的左孩子β與x的“親子關系”:β.parent=x AND x.right=β.
第二步:y取代x:(當x不是ROOT節點時) y.parent=x.parent AND (x.parent.left==x)? (y=x.parent.left): (y=x.parent.right).
(當x是ROOT節點時) y=ROOT
第三步:逆轉x,y父子關系: x=y.left AND x.parent=y;

1 LEFT-ROTATE(T, x)
2 y=x.right
3 x.right=y.left
4 if y.left != T.nil
5 y.left.p=x
6 y.parent=x.parent
7 if x.parent==T.nil
8 T.root=y
9 else if x==x.parent.left
10 x.parent.left=y
11 else x.parent.left=y
12 y.left=x
13 x.parent=y
4. 插入:
---------
紅黑樹的插入和BST基本類似。不清楚BST插入算法的可以參考我的這一篇博客:http://www.cnblogs.com/fu11211129/p/4214047.html
紅黑樹的插入在BST的插入上做了一些微調,這里把這兩種插入的偽代碼展示做個比較。

TREE-INSERT(T, z)//BST插入算法
1 y=NIL
2 x=T.ROOT
3 while(x!=NIL)
4 y=x
5 if(z.key<x.key)
6 x=x.left
7 else x=x.right
8 z.p=y
9 if(y==NIL) T.ROOT=z
10 else if(z.key<y.key) y.left=z
11 else y.right=z

RB-TREE-INSERT(T, z)//RBT插入算法
1 y=T.NIL
2 x=T.ROOT
3 while(x!=T.NIL)
4 y=x
5 if(z.key<x.key)
6 x=x.left
7 else x=x.right
8 z.p=y
9 if(y==T.NIL) T.ROOT=z
10 else if(z.key<y.key) y.left=z
11 else y.right=z
12 z.left=T.NIL
13 z.right=T.NIL
14 z.color=RED
17 RB-INSERT-FIXUP(T, z)
對比兩個插入算法,我們可以注意到四個不同,1)BST中的NIL被替換成了T.NIL,這是因為NIL在紅黑樹體系里是看做一個節點的。2)RBT-INSERT算法里14-15行,我們將T.NIL分別賦值給了z.left和z.right,是為了保持樹的合理結構。3)在RBT-INSERT算法里16行將z的顏色設置為紅色。4)因為給z着色以后可能會導致紅黑樹的基本性質被破壞,所以我們調用RB-INSERT-FIXUP函數(下面會具體講到)來秀姑RBT樹使之仍能保持它的基本性質。
5. 插入修復(這里用日本古代武士社會結構做比喻):
-------------------------------------------------
小日本武士中最大的叫將軍,管着很多大名,大名是有城有地盤,有兵有旗號,有很多家臣為其服務。家臣又分許多等級,大名的地盤分一部分給家臣,這就是城主。每個家臣都可以有自己的家臣,只要你地盤的收入俸祿養得起。這個等級制度,非常森嚴,沒有出身保證,就算功勞再大也爬不上去。因為除了將軍,武士一定得效忠於某個主公的,大名也是將軍的家臣。並且這種效忠是繼承的,你的子子孫孫都要效忠主公的后代。
日本和中國情況不同,沒有人敢喊出王候將相,寧有種乎。所以豐臣秀吉能從,最底層的普通武士做起,一步步做到關白(和將軍平級,那時沒設將軍,相當於將軍),成了不可思議的傳奇。當與你的功勞或能力所匹配的待遇,超過了主公所能給予,就可能發生改換門庭,甚至顛覆主公的事情。人們都說日本戰國時代就是一個下克上的時代。
好了,扯得有點遠了,回到正題來。我們把紅黑樹兩種顏色節點,代表兩個基本的武士類型,黑色代表安分守已對上機忠心耿耿,但同時也庸碌無為沒能力。紅色表示既有才能,又有野心。
插入操作時,好像一個新的武士出仕,沒有背景,沒有功勛。但他有能力,有抱負(紅色),就像豐臣秀吉那樣。要是跟了個黑色類型的主公,雖然終究會不甘心居人之下,但卻沒條件“下克上”,因為這個主公對大主公(主公的主公)忠心耿耿,兢兢業業,你再有才也英雄無用武之地,只能待時而動了。這種情況對應着RBT的結構已經穩定。
但要是主公是紅色的(有野心),那新武士就有想入非非了,成天跟大主公打小報告,說他謀反。反正主公忠心有限,把柄很多,只是大主公沒那個洞察力。大主公看到一些莫須有的證據,謀反這事寧枉勿縱。於是新武士舉報有功,主公反倒成了自己的家臣(如下圖第二個狀態)。但是昏庸的大主公很快就要付出代價,自己成了新武士的下一個目標(如下圖第三個狀態)。
(B表示新武士,A表示B的主公)
還有一種情況就是,主公的有野心的,但是大主公的其他家臣如果也有野心的話,情況就不一樣了,要是其他家臣也一樣有能力,那就熱鬧了,這一家從此兩虎相爭,不得安寧(此處略去數萬字)。大主公能力平庸,控制不住,最終釀成大亂,經過一番血雨腥風,大主公家出現了一位雄才偉略的家主,平定了內亂,原來兩家的強勢家主全部被消滅,家主換成了忠心可靠(變黑)的人。而大主公家的新家主,野心開始膨漲了(變紅)。
下面走一遍完整的流程:
好了從a)圖開始,新武士編號為4(這里我就不標紅圈了,陰影的就代表紅色)在家臣5的手下做事。家臣5不願意屈居人下,而4發現了這一點,4想攛掇5去篡權。但不幸的是,同樣是7的家臣,家臣8,也是野心勃勃(紅色)。所以在大名7的統治下,出現了混亂的局面,群雄四起。。。最終大名家族又出現了一名新的大名7,平定了內亂,將家臣5和8換成了對自己忠心的家臣。而且與此同時,新的大名7也是野心膨脹(變紅)。(在途中對應的是狀態1經過case1到達狀態2)
新的大名7野心勃勃,而且他的主公2也是野心不小,所以大名7只能戰而玩陰的了,大名7向大主公11說主公2的壞話。大主公也是二貨(黑色),聽風就是雨,大名7舉報有功,這樣一來,主公2反倒成了大名7的下屬了。但是很快的,這位二貨打主攻11也要自食惡果了,大名7直接占了他的位置。
6. 移植:
--------
因為移植操作是刪除操作的一個基本動作,所以這里先做個簡要說明。RBT的移植算法基本和BST的一致,大家可以參照博

RB-TRANPLANT(T, u, v)
1 if (u.parent==NIL) T.ROOT=v
2 else if(u==u.parent;.left) u.parent.left=v
3 else u.parent.right=v
4 v.parent=u.parent
客:http://www.cnblogs.com/fu11211129/p/4214047.html
大家可以發現只有第四行不一樣,原因很簡單,RBT把NIL看做一個節點了。
7. 刪除:
--------
刪除操作是比較麻煩的一部分內容,但類似的,和BST刪除操作也很相似,http://www.cnblogs.com/fu11211129/p/4214047.html
下面貼出RBT的刪除操作的偽代碼:

RB-DELETE(T, z) 單純刪除結點的總操作
1 if left[z] = nil[T] or right[z] = nil[T]
2 then y ← z
3 else y ← TREE-SUCCESSOR(z)
4 if left[y] ≠ nil[T]
5 then x ← left[y]
6 else x ← right[y]
7 p[x] ← p[y]
8 if p[y] = nil[T]
9 then root[T] ← x
10 else if y = left[p[y]]
11 then left[p[y]] ← x
12 else right[p[y]] ← x
13 if y ≠ z
14 then key[z] ← key[y]
15 copy y's satellite data into z
16 if color[y] = BLACK
17 then RB-DELETE-FIXUP(T, x)
因為相比BST刪除算法,就是加入一些節點顏色的處理機制,所以這里不再贅述。大家理解了BST的刪除后,RBT也就差不多了。
8. 刪除修復(還沒有想到合適的比喻,想到了會更新這一部分):
----------------------------------------------------------
還是先貼上偽代碼吧,然后結合代碼做個分析。

RB-DELETE-FIXUP(T, x) 恢復與保持紅黑性質的工作
1 while x ≠ root[T] and color[x] = BLACK
2 do if x = left[p[x]]
3 then w ← right[p[x]]
4 if color[w] = RED
5 then color[w] ← BLACK ▹ Case 1
6 color[p[x]] ← RED ▹ Case 1
7 LEFT-ROTATE(T, p[x]) ▹ Case 1
8 w ← right[p[x]] ▹ Case 1
9 if color[left[w]] = BLACK and color[right[w]] = BLACK
10 then color[w] ← RED ▹ Case 2
11 x p[x] ▹ Case 2
12 else if color[right[w]] = BLACK
13 then color[left[w]] ← BLACK ▹ Case 3
14 color[w] ← RED ▹ Case 3
15 RIGHT-ROTATE(T, w) ▹ Case 3
16 w ← right[p[x]] ▹ Case 3
17 color[w] ← color[p[x]] ▹ Case 4
18 color[p[x]] ← BLACK ▹ Case 4
19 color[right[w]] ← BLACK ▹ Case 4
20 LEFT-ROTATE(T, p[x]) ▹ Case 4
21 x ← root[T] ▹ Case 4
22 else (same as then clause with "right" and "left" exchanged)
23 color[x] ← BLACK
前面,我已經說了,因為插入、或刪除結點后,可能會違背、或破壞紅黑樹的原有的性質,所以為了使插入、或刪除結點后的樹依然維持為一棵新的紅黑樹,那就要做倆方面的工作:1、部分結點顏色,重新着色 2、調整部分指針的指向,即左旋、右旋。而下面所有的文字,則是針對紅黑樹刪除結點后,所做的修復紅黑樹性質的工作。
情況1:當前節點的兄弟節點是紅色;解法,在當前節點的父節點上進行左旋,結束。此時紅黑樹性質全部恢復。
情況2:當前節點的兄弟節點是黑色並且兄弟節點的孩子節點也都是黑色 解法:直接把兄弟節點變成紅色就行了。
情況3:當前節點的兄弟節點是黑色,且兄弟節點的左孩子為紅色,右孩子為黑色。解法:在兄弟節點的左孩子上右旋,並且互換父親左孩子的顏色。
情況3:當前節點的兄弟節點是黑色,且兄弟節點的兩個孩子都為紅色。解法: 在父親節點上左旋,並且互換父親右孩子顏色。
(日本武士版解釋:在介紹插入的時候,我們“插入故事”的“主人翁”是一個剛出道的武士,雄姿英發,羽扇綸巾。。。,在這里我們“刪除故事”的主人翁是一個“要退役”的武士x,但是秉承武士“猥瑣的”特點,x下崗之前,總要把格局教的混亂不堪才行。但是因為x要“退役”了,所以他自己是沒有能力的(黑色)。所以他把目光轉移到了他的兄弟上,但這也要看他的兄弟w是個什么貨色
如果他胸無大志,那么x就撩起兄弟w的野心(變紅)(對應case2)。
如果他的兄弟w是有野心的(紅色),x也就不用攛掇了,但是兵法有雲,攘外必先安內(我也不知道是不是兵法上的,姑且這么說吧),如果w的兩個家臣都安分守己,那么好,w就可以放心去搶班奪權了(對應case1),w成功之后志得意滿,生活墮落(變黑),而被w強權的曾經上級B開始卧薪嘗膽了(變紅)(對應case1)。
如果他的兄弟w胸無大志,但是他的其中一個家臣野心不小,那么x就轉而開始攛掇w的家臣犯上作亂(對應case3)。
如果他的兄弟w胸無大志,而且悲催的是,他的家臣沒有一個真正沉服於他的。那么x心想我不用攢多了,讓他們內斗吧,最后D家族出了一位新的w,先是成功篡權,然后將自己的手下換成對自己中心的人(黑色)