若被刪除的結點有兩個非葉子結點,那么可以轉換為刪除一個“替代點”的問題,該替代點最多只有一個非葉子孩子結點。可以通過前驅或者后繼(都最多有一個非葉子孩子結點)來替代最初要被刪除的結點,所以下面只關注只有一個非葉子孩子結點的問題,一旦我們解決了這個問題,那么解決方法將同樣適用於兩種情形:
1、原本想刪除的結點最多有一個非葉子孩子結點
2、原本想刪除的結點有兩個非葉子孩子結點(通過前驅和后繼可以轉化為最多有一個非葉子孩子結點)
好了,現在用N來表示替代結點(要是被刪除的結點沒有替代結點,那么N就表示原本要被刪除的結點,由紅黑樹的屬性可以推出該結點不可能有孫子結點),替代結點要么是前驅,要么是后繼,但是都最多只有一個非葉子孩子結點(程序中使用的是前驅)。結點N為不同顏色時,處理方式不一樣,具體如下。
結點N為紅色:
若結點N為紅色,那么它的孩子結點必須是黑色的,而且兩個孩子結點都是葉子結點(因為結點N最多只能有一個非葉子孩子結點,所以只能是一邊是一個黑色的非葉子孩子結點,另一邊是一個黑色葉子結點,這樣通過結點N的路徑的黑色結點的數目就不一樣了(因為黑色的非葉子孩子結點至少有兩個黑色葉子結點),這樣就違反了性質5)。此時直接將N刪除即可,刪除之后N被空葉子結點(NULL)替代,而NULL結點都是黑色的,所以屬性5依然保持。好了,結點N為紅色的情形比較簡單,下面看結點N為黑色的情形。
結點N為黑色:
結點N為黑色時,可以分為兩種情況:
第1種情況:若其孩子結點M有一個是非葉子結點,另外一個是黑色空葉子結點,那么非葉子結點M必須是紅色。因為如果非葉子結點也是黑色的,這樣通過結點N的路徑的黑色結點的數目將不一樣(通過空葉子結點這邊的黑色結點的數目是2,而通過黑色非葉子結點這邊的至少是3),由此違反了性質5。所以非葉子結點M只能是紅色的,這種情況比較簡單,只需用M代替N,並將M變為黑色,則不違反任何屬性,此時依然是一棵合格的紅黑樹。
第2種情況:若兩個孩子結點都為空葉子結點(葉子結點都為黑色,並且是NULL),則此時用空葉子結點NULL替代N后,通過原始替代結點N的路徑的黑色結點的數目將比原來減少1,此時就要分類討論了,將替代結點N的父結點表示為P,兄弟結點表示為S。具體分類如下:
case 1:
N是根結點,在這種情況下,我們已經做完了(僅僅通過直接返回就可以達到這個效果!),因為這種情況下整棵樹只有一個結點(注意大前提:N的兩個孩子結點都是空葉子結點,所以整棵樹只有一個結點),要刪除的結點正是根結點,直接返回之后,在上一層函數中用N的子結點(NULL)替代N,相當於將N刪除掉了。
#294#_
|
~317~
Deleting key 1th: 294 ---------------------------------------------------------------------
case 0 <294> (若當前結點的子結點為紅色,則將子結點變成黑色) :
#294#_
|
#317#
case 0 <294> (將當前結點用子結點替代,之后釋放當前結點) :
#317#
Deleting key 1th: 317 ---------------------------------------------------------------------
case 1 <317> (若當前結點是根結點,直接返回) :
case 0 <317> (將當前結點用子結點替代,之后釋放當前結點) :
NODE is NULL
注意:在case 2,case 5,case 6中,我們假設N是父結點P的左孩子結點,如果是右孩子結點,那么“左”和“右”應該對調。
case 2:
N的兄弟結點S是紅色的(從case 3開始S都是黑色的)。此時對調父結點P(肯定是黑色的)和兄弟結點S的顏色,然后將父結點P左旋。注意此時如果直接將N刪掉而結束,那么P結點的左邊只有一個黑色結點(NULL),而右邊有兩個黑色結點(S的左孩子和S的孫子結點(黑色的空葉子結點)),這樣就違反了屬性5,所以左旋之后將接着按case 4、case 5或case 6來處理。
________________#177#__________________________
| |
_#103#______ ________________~265~______
| | | |
#16# _~146~_ _#206#______ _#313#_
| | | | | |
#140# #170# #178# _~236~_ #294# #317#
| |
#208# #253#
Deleting key 1th: 16 ---------------------------------------------------------------------
case 2 <16> (若當前結點的兄弟結點是紅色,則將父親和兄弟的顏色對調,若該結點靠左,則左旋父結點,若靠右則右旋父結點) :
________________#177#__________________________
| |
_~103~______ ________________~265~______
| | | |
#16# _#146#_ _#206#______ _#313#_
| | | | | |
#140# #170# #178# _~236~_ #294# #317#
| |
#208# #253#
左旋: 103
______#177#__________________________
| |
______#146#_ ________________~265~______
| | | |
_~103~_ #170# _#206#______ _#313#_
| | | | | |
#16# #140# #178# _~236~_ #294# #317#
| |
#208# #253#
case 4 <16> (若當前結點的父結點為紅色,兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點和父結點的顏色對調) :
______#177#__________________________
| |
______#146#_ ________________~265~______
| | | |
_#103#_ #170# _#206#______ _#313#_
| | | | | |
#16# ~140~ #178# _~236~_ #294# #317#
| |
#208# #253#
case 0 <16> (將當前結點用子結點替代,之后釋放當前結點) :
______#177#__________________________
| |
______#146#_ ________________~265~______
| | | |
#103#_ #170# _#206#______ _#313#_
| | | | |
~140~ #178# _~236~_ #294# #317#
| |
#208# #253#
case 3:
結點N的父結點P,兄弟結點S以及S的孩子結點都是黑色的。此時簡單的將S設為紅色,這樣做可以讓通過S的所有路徑都少一個黑色節點,與通過N的路徑的黑色結點就相同了。但是,通過P的所有路徑現在比不通過P的路徑少了一個黑色節點,所以仍然違反性質5。要修正這個問題,我們要從case 1開始,在P上做重新平衡處理,並且一直遞歸到根結點為止,到根結點時,通過根結點的所有路徑都少了一個黑色結點,然后再從case 1返回。
______#236#______
| |
_#178#_ _#294#_
| | | |
#177# #206# #253# #317#
Deleting key 5th: 253 ---------------------------------------------------------------------
case 3 <253> (若當前結點的父結點和兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點變紅,將父結點轉到case 1做重新平衡處理) :
______#236#______
| |
_#178#_ _#294#_
| | | |
#177# #206# #253# ~317~
case 3 <294> (若當前結點的父結點和兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點變紅,將父結點轉到case 1做重新平衡處理) :
______#236#______
| |
_~178~_ _#294#_
| | | |
#177# #206# #253# ~317~
case 1 <236> (若當前結點是根結點,直接返回) :
case 0 <253> (將當前結點用子結點替代,之后釋放當前結點) :
______#236#_
| |
_~178~_ #294#_
| | |
#177# #206# ~317~
case 4:
結點N的父結點P是紅色的,兄弟結點S以及S的兩個孩子結點(都是空葉子結點)都是黑色。此時將P和S的顏色對調,這不影響通過S的黑色結點數量,但是在通過N的路徑上增加了一個黑色結點,正好和要刪除的結點N相抵消,這樣屬性5被滿足了。
________________#236#______
| |
______#177#______ _#294#_
| | | |
#146#_ _~206~_ #253# #317#
| | |
~170~ #178# #208#
Deleting key 6th: 208 ---------------------------------------------------------------------
case 4 <208> (若當前結點的父結點為紅色,兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點和父結點的顏色對調) :
________________#236#______
| |
______#177#______ _#294#_
| | | |
#146#_ _#206#_ #253# #317#
| | |
~170~ ~178~ #208#
case 0 <208> (將當前結點用子結點替代,之后釋放當前結點) :
___________#236#______
| |
______#177#______ _#294#_
| | | |
#146#_ _#206# #253# #317#
| |
~170~ ~178~
case 5:
結點N的兄弟結點S的左孩子是紅色,右孩子是黑色(左紅右黑),並且結點N是父結點P的左孩子。此時將S與其左孩子的顏色對調,然后右旋S。注意此時通過父結點P的路徑都有相同的黑色結點數目,但是如果直接將N刪掉會違反屬性5(與case 2中類似),所以我們繼續按照case 6來處理。
___________#236#______
| |
_#177#______ _#294#_
| | | |
#170# _#206# #253# #317#
|
~178~
Deleting key 1th: 170 ---------------------------------------------------------------------
case 5 <170> (subcase1: 若當前結點為左子結點,兄弟結點是黑色,且兄弟結點的左子結點為紅色,右子結點為黑色,則將兄弟結點和兄弟結點左孩子的顏色對調,再右旋兄弟結點) :
___________#236#______
| |
_#177#______ _#294#_
| | | |
#170# _~206~ #253# #317#
|
#178#
右旋: 206
___________#236#______
| |
_#177#_ _#294#_
| | | |
#170# #178#_ #253# #317#
|
~206~
case 6 <170> (將兄弟結點的顏色和父結點的顏色對調) :
___________#236#______
| |
_#177#_ _#294#_
| | | |
#170# #178#_ #253# #317#
|
~206~
case 6 <170> (subcase1: 若當前結點為左子結點,則兄弟結點的右子結點必為紅色,將兄弟結點的右子結點變成黑色,將父結點左旋) :
___________#236#______
| |
_#177#_ _#294#_
| | | |
#170# #178#_ #253# #317#
|
#206#
左旋: 177
______#236#______
| |
_#178#_ _#294#_
| | | |
_#177# #206# #253# #317#
|
#170#
case 0 <170> (將當前結點用子結點替代,之后釋放當前結點) :
______#236#______
| |
_#178#_ _#294#_
| | | |
#177# #206# #253# #317#
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#313#_
| | | |
#178# _~236~_ #294# #317#
| |
#208# #253#
Deleting key 12th: 313 ---------------------------------------------------------------------
若結點有兩個非空結點,用前驅代替它
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#294#_
| | | |
#178# _~236~_ #294# #317#
| |
#208# #253#
case 3 <294> (若當前結點的父結點和兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點變紅,將父結點轉到case 1做重新平衡處理) :
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#294#_
| | | |
#178# _~236~_ #294# ~317~
| |
#208# #253#
case 5 <294> (subcase2: 若當前結點為右子結點,兄弟結點是黑色,且兄弟結點的左子結點為黑色,右子結點為紅色,則將兄弟結點和兄弟結點右孩子的顏色對調,再左旋兄弟結點) :
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _~206~______ _#294#_
| | | |
#178# _#236#_ #294# ~317~
| |
#208# #253#
左旋: 206
______#177#__________________________
| |
_#146#_ ______~265~______
| | | |
#103# #170# ______#236#_ _#294#_
| | | |
_~206~_ #253# #294# ~317~
| |
#178# #208#
case 6 <294> (將兄弟結點的顏色和父結點的顏色對調) :
______#177#__________________________
| |
_#146#_ ______#265#______
| | | |
#103# #170# ______~236~_ _#294#_
| | | |
_~206~_ #253# #294# ~317~
| |
#178# #208#
case 6 <294> (subcase2: 若當前結點為右子結點,則兄弟結點的左子結點必為紅色,將兄弟結點的左子結點變成黑色,將父結點右旋) :
______#177#__________________________
| |
_#146#_ ______#265#______
| | | |
#103# #170# ______~236~_ _#294#_
| | | |
_#206#_ #253# #294# ~317~
| |
#178# #208#
右旋: 265
______#177#________________
| |
_#146#_ ______~236~______
| | | |
#103# #170# _#206#_ _#265#______
| | | |
#178# #208# #253# _#294#_
| |
#294# ~317~
case 0 <294> (將當前結點用子結點替代,之后釋放當前結點) :
______#177#________________
| |
_#146#_ ______~236~______
| | | |
#103# #170# _#206#_ _#265#_
| | | |
#178# #208# #253# #294#_
|
~317~
case 6:
結點N的兄弟結點S是黑色的,S的右孩子是紅色的(右紅),並且結點N是父結點P的左孩子。此時將父結點P和兄弟結點S的顏色對調,再將S的右孩子變為黑色,然后左旋父結點P。
________________#177#__________________________
| |
_____#103#______ ________________~265~______
| | | |
#15#_ _~146~_ _#206#______ _#294#_
| | | | | | |
~16~ #140# #170# #178# _~236~_ #293# #313#_
| | |
#208# #253# ~317~
Deleting key 14th: 293 ---------------------------------------------------------------------
case 6 <293> (將兄弟結點的顏色和父結點的顏色對調) :
________________#177#__________________________
| |
_____#103#______ ________________~265~______
| | | |
#15#_ _~146~_ _#206#______ _#294#_
| | | | | | |
~16~ #140# #170# #178# _~236~_ #293# #313#_
| | |
#208# #253# ~317~
case 6 <293> (subcase1: 若當前結點為左子結點,則兄弟結點的右子結點必為紅色,將兄弟結點的右子結點變成黑色,將父結點左旋) :
________________#177#__________________________
| |
_____#103#______ ________________~265~______
| | | |
#15#_ _~146~_ _#206#______ _#294#_
| | | | | | |
~16~ #140# #170# #178# _~236~_ #293# #313#_
| | |
#208# #253# #317#
左旋: 294
________________#177#__________________________
| |
_____#103#______ ________________~265~___________
| | | |
#15#_ _~146~_ _#206#______ _#313#_
| | | | | | |
~16~ #140# #170# #178# _~236~_ _#294# #317#
| | |
#208# #253# #293#
case 0 <293> (將當前結點用子結點替代,之后釋放當前結點) :
________________#177#__________________________
| |
_____#103#______ ________________~265~______
| | | |
#15#_ _~146~_ _#206#______ _#313#_
| | | | | | |
~16~ #140# #170# #178# _~236~_ #294# #317#
| |
#208# #253#
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#313#_
| | | |
#178# _~236~_ #294# #317#
| |
#208# #253#
Deleting key 12th: 313 ---------------------------------------------------------------------
若結點有兩個非空結點,用前驅代替它
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#294#_
| | | |
#178# _~236~_ #294# #317#
| |
#208# #253#
case 3 <294> (若當前結點的父結點和兄弟結點是黑色,且兄弟結點的左右子結點都為黑色,則將兄弟結點變紅,將父結點轉到case 1做重新平衡處理) :
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _#206#______ _#294#_
| | | |
#178# _~236~_ #294# ~317~
| |
#208# #253#
case 5 <294> (subcase2: 若當前結點為右子結點,兄弟結點是黑色,且兄弟結點的左子結點為黑色,右子結點為紅色,則將兄弟結點和兄弟結點右孩子的顏色對調,再左旋兄弟結點) :
______#177#__________________________
| |
_#146#_ ________________~265~______
| | | |
#103# #170# _~206~______ _#294#_
| | | |
#178# _#236#_ #294# ~317~
| |
#208# #253#
左旋: 206
______#177#__________________________
| |
_#146#_ ______~265~______
| | | |
#103# #170# ______#236#_ _#294#_
| | | |
_~206~_ #253# #294# ~317~
| |
#178# #208#
case 6 <294> (將兄弟結點的顏色和父結點的顏色對調) :
______#177#__________________________
| |
_#146#_ ______#265#______
| | | |
#103# #170# ______~236~_ _#294#_
| | | |
_~206~_ #253# #294# ~317~
| |
#178# #208#
case 6 <294> (subcase2: 若當前結點為右子結點,則兄弟結點的左子結點必為紅色,將兄弟結點的左子結點變成黑色,將父結點右旋) :
______#177#__________________________
| |
_#146#_ ______#265#______
| | | |
#103# #170# ______~236~_ _#294#_
| | | |
_#206#_ #253# #294# ~317~
| |
#178# #208#
右旋: 265
______#177#________________
| |
_#146#_ ______~236~______
| | | |
#103# #170# _#206#_ _#265#______
| | | |
#178# #208# #253# _#294#_
| |
#294# ~317~
case 0 <294> (將當前結點用子結點替代,之后釋放當前結點) :
______#177#________________
| |
_#146#_ ______~236~______
| | | |
#103# #170# _#206#_ _#265#_
| | | |
#178# #208# #253# #294#_
|
~317~