紅黑樹的刪除操作是紅黑樹最複雜的操作了,只要搞明白這個,基本上就明白紅黑樹的調整。刪除的情況大致可以分爲四種。
case 1. .刪除節點z,,子節點少於兩個時,左兒子爲T.NIL,用右兒子代替z
case 2. 刪除節點z,子節點少於兩個時,右兒子爲T.NIL,用左兒子代替z
case 3 刪除節點z,有兩個子節點時,找出後繼節點y,如果後繼節點y是右兒子,直接用y子樹替換z節點
case 4刪除節點z,有兩個子節點時,找出後繼節點y,如果後繼節點不是右兒子,用後繼節點的右兒子x子樹替換y子樹,再用後繼節點y子樹替換z節點。
下面看看圖形展示這四種刪除情況,其中z節點表示想要刪除的節點,y節點表示真正刪除的節點(從刪除節點的顏色角度來看),x表示要替換y的節點。由於y的顏色可能改變,用y-original-color表示y顏色改變之前的顏色
僞代碼如下:
RB-DELETE(T, z)
y = z;
y-original-color = y.color;
if z.left == T.NIL
x = z.right;
RB-TRANSPLANT(T, z, z.right);
else if z.right == T.NIL
x = z.left;
RB-TRANSPLANT(T, z, z.left);
else y = TREE-MINMUM(z.right)
y-original-color = y.color;
x = y.right;
if y.p = z;
x.p = y;
else RB-TRANSPLANT(T, y, y.right)
y.right = z.right;
y.right.p = y;
RB-TRANSPLANT(T, z, y)
y.left = z.left;
y.left.p = y;
y.color = z.color;
if y-original-color == black
RB-DELETE-FIXUP(T, x)
可以看到,如果y是黑色的,就肯定破壞了紅黑樹的性質了。如果y是紅色的,紅黑樹性質依然保持,因爲樹的黑高沒有改變,也不存在兩個相鄰的的紅色節點,性質4和5沒有任何改變。所以y是黑色的時候需要通過RB-DELETE-FIXUP來調整修正。下面來看看如何調整。
違反性質2的情況:如果y是原來的根節點,而y的一個紅色孩子成爲新的根幾點,就違反了性質2,但是這很容易解決,直接染黑就是了,不再繼續討論這種情況。
違反性質4的情況:x節點爲紅色,x的父節點也爲紅色,這種情況也很容易解決,染黑x就是了。
剩下的情況就是隻違反了性質5,只要調整好性質5就好了,這裏有一個技巧,就是把x節點視爲還有一層黑色,問題就變成了解決違反性質1了,也就是把x看成既紅又黑,我們只要把這層額外的黑色不斷往上推,直到推給了一個紅色節點,那麼子樹的黑高就恢復了。和插入一樣,有個關鍵思想是,轉換過程中千萬不能破壞其他任何的性質。經過分析,破壞性質1(本質上是破壞性質5)有以下五種情況:
case 1 x是紅色的
case 2. x的兄弟節點w是紅色的
case 3 x的兄弟節點w是黑色的,而且w的兩個子節點都是黑色的
case 4 x的兄弟節點w是黑色的,w的左兒子是紅色的,w的右孩子是黑色的
case 5 x的兄弟節點w是黑色的,且w的右孩子是紅色的。
如下圖所示:
case 1是最容易解決的,直接染黑就是了。
case 2的話,改變w和x.p的顏色,左旋轉x.p,這樣子不改變任何性質的同時,把case 2轉變爲case 3,4,5。不做詳細討論
僞代碼爲:
w.color = black;
x.p.color = red;
LEFT-ROTATE(T, x.p)
w = x.p.right;
case 3的話,可以認爲從x和w去掉一層黑色給x.p,如果x.p爲原本爲紅色的話,那麼x的子樹黑高加一,w子樹黑高不變,性質就恢復好了,如果x.p原來爲黑色的,那麼認爲x.p的整個子樹黑高都少了1,多了的一層黑色就給了x.p,case3就轉爲case 2,3,4,5了。
僞代碼如下:
w.color = red;
x = x.p
case 4的情況左侄兒爲紅,右侄兒爲黑,這種情況統一轉case 5來處理。
這裏右旋w並且沒有改變紅黑樹的五大性質,轉爲了case5。僞代碼如下:
w.left.color = black;
w.color = red;
RIGHT-ROTATE(T, w)
w = x.p.right;
case 5的情況是紅黑樹調整的出口,只要到達了case 5,調整完就能恢復所有性質了。調整如下圖所示:
接下來分析case5的轉換過程,這裏的思路是這樣的:首先我們要讓x子樹黑高加一,那麼就左旋轉a,左旋轉後d的左子樹沒有任何問題,但是右子樹黑高可能減少了1(如果a原來是黑色的情況),爲了解決這個問題,可以把a和d顏色交換,然後染黑c,這樣左旋轉後的d的右子樹的黑高也就不會有任何改變了。僞代碼如下:
w.color = x.p.color;
x.p.color = black;
w.right.color = black;
LEFT-ROTATE(T, x.p);
x = T.root;
爲了能在全局下看到調整情況,下面展示整個刪除調整的過程僞代碼:
RB-DELETE-FIXUP(T, x)
while x != T.root && x.color = black
if x == x.p.left
w = x.p.right
// case 2
if w.color = red
w.color = black;
x.p.color = red;
LEFT-ROTATE(T, x.p)
w = x.p.right;
// case 3
if w.left.color == black && w.right.color == black
w.color = red;
x = x.p;
// case 4
else if w.right.color == black
w.left.color = black;
w.color = red;
RIGHT-ROTATE(T, w)
w = x.p.right;
// case 5
w.color = x.p.color;
x.p.color = black;
w.right.color = black;
LEFT-ROTATE(T, x.p);
x = T.root;