查閱了很多的資料,最終把紅黑樹的刪除操作弄明白。為方便日后溫習,也為那些正想弄明白但又苦苦明白不了該操作詳細原理的同學查閱,特將其當筆記記錄下來。下面內容是個人所理解的紅黑樹的刪除操作,如果有不對的地方,歡迎各路大神指正。
紅黑樹的刪除操作
1:節點命名約定
D表示要被刪除的節點。即:取 Delete 的首字母;
P 表示父節點。即:取 Parent 的首字母;
S表示兄弟姐妹節點。即:取 Sibling的首字母;
U表示叔伯節點。即:取Uncle的首字母;
G表示祖父節點。即:取 Grandfather的首字母;
L表示左樹。即:取Left的首字母;
R表示右樹。即:取Right的首字母;
Nil表示葉子節點。即所謂的空節點;注意:紅黑樹中的葉子節點與其他樹中所述說的葉子節點不是同一概念。而且紅黑樹中的葉子節點(即:Nil節點)永遠是被定義為黑色的。
下文的節點命名表示將會使用以上這些命名約定或它們的組合表示。因此,請先牢記這些命名約定。舉例:
DR表示要被刪除的節點的右子樹,即:右子節點;
SL表示兄弟節點的左子樹,即:左子節點;
…
2:刪除操作宏觀分析
在紅黑樹中,刪除一個節點往大的說,只有以下四種情況。
情況一:刪除的節點的左、右子樹都非空;
情況二:刪除的節點的左子樹為空樹,右子樹非空;
情況三:刪除的節點的右子樹為空樹,左子樹非空;
情況四:刪除的節點的左、右子樹都為空樹;
其中情況一,可以按與其他二叉搜索樹的刪除方式一樣處理,最終可以轉換到后面的三種情況。具體為:找到(Old)D節點的直接后繼節點(暫且稱為X節點),然后將X的值轉移到D節點,最后將X節點作為真正要被刪除掉的節點(即:(Real)D節點)。這樣刪除操作后,可以保證該樹仍然為一棵二叉搜索樹。但由於紅黑樹的定義(即:紅黑樹的性質)約定。這樣刪除(Real)D節點后,可能會破壞紅黑樹的性質。所以需要額外做一些調整處理,這便是下面將要詳細討論的內容。
說明:下文中所提到的D,除非有特別說明,否則都將指的是(Real)D。
3:紅黑樹刪除后平衡處理
在具體分析之前,再次列出紅黑樹的定義:
1) 任何一個節點非紅即黑;
2) 樹的根為黑色;
3) 葉子節點為黑色(注意:紅黑樹的所有葉子節點都指的是Nil節點);
4) 任何兩個父子節點不可能同時為紅色;
5) 任何節點到其所有分枝葉子的簡單路徑上的黑節點個數相同;
下面是幾個圖示說明:
根據紅黑樹的定義,被刪除的節點D(即:上文所述的(Real)D節點)不論如何都一定有一個“右子樹”,只是該右子樹要不為非空樹(即:真正存在的節點,不為Nil節點),要不就必為空樹(即:D的兩個子節點都為Nil)。下面稱D的該右子節點(或稱為右子樹)為DR。
a) 被刪除的D節點為紅色。這種情況,則與D相關的顏色以及結構關系必然只有如下一種情況(為什么只有這種情況,不明白的請看紅黑樹的性質):
分析:因為D為紅色,所以P必為黑色,同時DR不可能為紅色(否則違反性質4)。同時由於性質5,則DR必為Nil,否則就D樹來說,經過DR與不經過DR的路徑的黑節點數必不相同。現在要刪除D節點,只需要直接將D節點刪除,並將DR作為P的左子節點即可。因此刪除后,變成上圖右側所示。
b) 被刪除的D節點為黑色。此時情況會稍復雜些,具體又分析為:DR為Nil與DR不為Nil。根據性質5,如果DR不為Nil,則DR必為紅色,且DR的兩個子節點必為Nil。因此,此處先來分析DR不為Nil的情況(因為該情況比較簡單)。而DR為Nil的情況,由后面的C)及其后內容再進行具體分析 。
如前所述,如果DR不為Nil,則D、DR必為如下情況:
分析:由於刪除的D為黑色,刪除后P的左子樹的黑節點數必少1,此時剛好DR為黑色,並且刪除后DR可以占據D的位置(這樣仍是一棵二叉搜索樹,只是暫時還不是合格的紅黑樹罷了),然后再將DR的顏色改為黑色,剛好可以填補P左子樹所減少的黑節點數。從而P樹又平衡了。因此,平衡處理后,最終變成上圖右側的圖示。
c) 被刪除的D為黑色,且DR為Nil。
如果DR為Nil,則刪除D后,P的左子樹黑節點數必定少1,純粹想在P的左子樹做文章來平衡P樹是絕無可能的了。因此,必定需要其他分支的輔助來最終完成平衡調整。根據紅黑樹的定義,P會有一個右子節點,稱為S子節點。此處又可細節分兩種情況:黑S與紅S。此處先討論紅S的情況。
說明:如果S為黑,則它必不會為Nil。(不明白的人,再好好想想為什么)。同時根據紅黑樹的性質,D、S、P、SL、SL的顏色關系必只有如下一種情況(因為此處探討的是S為紅的情況)。
分析:刪除前P樹的左、右子樹的黑節點數平衡,刪除后(即:上圖右側所示),經過DR分支的黑節點數將比通過S分支的黑節點數少1。此時,做如下操作:
將P左旋轉,再將P由黑色改為紅色,將S由紅色改為黑色,演變過程如下圖示:
經過以上演變后,經過P的路徑,左分支黑節點數仍是少1,其他分支的黑節點數做仍然保持不變。此時的情況卻變成DR的兄弟節點為黑色(不再是紅色的情況了),因此轉入此處c)點一開始所說的另一種情況(S為黑色的情況)的處理。
提示:此時需要把 SR 節點看成原先的 S 節點。
注意:可能有人會一時想不明白什么要這樣轉換。因為這樣轉換后,雖然對於P樹的左子樹的黑節點數仍然會比右子樹的黑節點數少1,但此時DR的兄弟(以前的S節點)現在已經變為SL,即已經由紅色變為黑色,並且非常重要的此時的DR的兄弟節點SL的子結點(即:DR的兩個侄子節點),要不就是紅色節點要不就必為Nil節點,而這種情況正是D為黑色、S也黑色的情況。(注意看注意看注意看一定注意看這點:對於被刪除節點D的父節點來說,D黑S黑的情況下,無論如何D的兄弟節點S的兩個兒子節點SL與SR都不可能是非Nil的黑節點。不明白的好好想想為什么)。因此我們有了進一步分析的余地。
d) 被刪除的D為黑色,S也為黑色的情況。根據D、P、S、SL、SR的顏色組合情況,本來是有非常多種變換的。但事實上,我們只需要按如下4種情況做進一步的處理,即可全部涵蓋所有的顏色組合情況。
d.1> SL為紅,SR顏色任意;(對於該情況的處理,其實我們不關心P的顏色)
d.2> SR為紅,SL顏色任意;(對於該情況的處理,其實我們不關心P的顏色)
d.3> SL、SR都為黑;P為紅。(注意:根據前面c)點的紅色文字部分的分析,此時SL與SR必定必定都為Nil節點);
d.4> SL、SR都為黑;P為黑。(注意:根據前面c)點的紅色文字部分的分析,此時SL與SR必定必定都為Nil節點);
d.1> SL為紅,SR顏色任意情況
分析:P樹的左子樹黑節點數減少1,因此,要想平衡,必需想辦法讓左子樹的黑結節數增加1,而且同時P的右子樹的黑節點數要保持不變。因此,想辦法將SL這個紅色節點利用起來,讓它來填補P的左子樹所缺少的黑節點個數。因此,立馬想到旋轉,只要有辦法轉到P的左子樹或P位置上,則就有可能填平P左子樹的高度。所以具體操作步驟為:
將S右旋轉;接着將SL改為P的顏色,P的顏色改為黑色(用這個黑色來填補DR分支的黑節點數);將P左旋轉。
d.2> SR為紅色,SL顏色任意的情況
分析:思路同d.1>情況類似,都是想辦法用紅色的SR節點來填補P的左子樹的減少的黑節點數。具體步驟為:
將S由黑色改為P的顏色;
將SR由紅色改為黑色;
將P的顏色改為黑色(用該黑色來填補DR分支缺失的黑節點數);
將P節點左旋轉;
d.3> SL、SR都為黑色(其實都為Nil節點),P為紅色的情況
分析:此情況較為簡單,直接將紅色的P改為黑色,以此為填補DR缺少的黑節點個數。此時P右子樹黑節點數卻增多,因此,再將S改為紅色即可平衡。
d.4> SL、SR都為黑色(其實都為Nil節點),P為黑色的情況
分析:因為DR、P、S、SL、SR全都為黑色,則不論如何變換,都永遠不可能使用P的左右子樹的黑節點數達到平衡。而P不平衡的原因是因為P的右子樹黑節點數會比左子樹多1個。因此,干脆將S由黑色改為紅色,如此一來,P的左、右子樹的黑節點個數是平衡的。但是,此時經過P的路徑的黑節點個數將會比不經過它的路徑少一個。因此,我們將P作為待平衡的節點(即:此時的P相當於DR的角色)往上繼續上溯,直到P為根節點為止。
到此,紅黑樹的刪除操作已全部說明完。從以上的分析來看,紅黑樹的刪除操作將是最為費時的,至少會比插入操作復雜,更耗時的可能性肯定會更大。但事實上,紅黑樹的插入、刪除、效率都是非常高的。插入比較簡單就不說了。刪除操作事實上,就算遇到最壞情況:DR、P、S、SL、SR全為黑的情況下,在上溯過程中,一般情況下也是會很快就遇到紅色的P節點的。除非整棵紅黑樹的右子樹基本上都是黑節點(試想下,這可能性幾乎不可能,因為我們插入時,總是以紅色插入的,再加上紅黑樹的幾條性質約束一下),或者本來身紅黑樹的整體高度就非常淺。
以上是個人的理解,歡迎指正。