红黑树的删除操作花费O(lg n)时间,删除算法与二叉搜索树的删除类似,首先红黑树的TRANSPLANT版本有些许不同,主要是因为红黑树使用nil结点代替NULL指针造成的:
RB-TRANSPLANT(T, u, v)
if u.p ==T.nil
T.root= v
else if u== u.p.left
u.p.left= v
else u.p.right= v
v.p = u.p
删除操作的代码如下:
RB-DELETE(T.z)
y = z
y-original-color = y.color
if z.left == T.nil
x = z.right
RB-TRANSPLAN(T, z, z.right)
else if z.right == T.nil
x = z.left
RB-TRANSPLAN(T, z, z.left)
else y = TREE-MINIMUM(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)
调整代码如下:
RB-DELETE-FIXUP(T, x)
while x != T.root and x.color == BLACK
if x == x.p. left
w = x.p.right
if w.color == RED
w.color =BLACK // case 1
x.p.color =RED // case 1
LEFT-ROTATE(T,x.p) // case 1
w = x.p.right // case1
if w.left.color ==BLACK and w.right.color == BLACK
w.color= RED //case 2
x= x.p //case 2
else if w.right.color == BLACK
w.left.color = BLACK // case 4
w.color = RED // case 4
RIGHT-ROTATE(T,w) // case 4
w = x.p.right // case 4
w.color = x.p.color // case 3
x.p.color = BLACK // case 3
w.right.color = BLACK // case 3
LEFT-ROTATE(T, x.p) // case 3
x = T.root // case 3
else(same as then clause with “right” and “left” exchanged)
……
x.color= BLACK
根据代码,可以将删除操作分为四种情况(等同于二叉搜索树的删除),代码中的x, y, z的意思如下:
z表示要删除的元素;
y表示实际要删除的元素,对于前两种情况(z只有一个孩子),y就是z;对于后两种情况(z有俩孩子),y是z的后继元素。
x表示删除y之后,代替y所在位置的元素。
具体情况如下图, :
情况一,二很简单,不再赘述,对于情况三和四,表面上看好像是删除了结点z,实际上是删除了结点y,因为在结点z的位置上,下面的代码保证了:除了z.key变成了y.key,其他的属性left, right, p, color都没有发生实质的变化。 所以,在红黑树中,实际上是删除了结点y,而结点x则成为在y所在位置的新节点。
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
得出上面的结论后,就看下删除y结点之后,给红黑树的性质带来了哪些变化:
如果y的颜色是RED,则y不可能为红黑树的根,所以不管x的颜色是什么,都不会影响到红黑树的性质。所以只考虑y的颜色为BLACK的情况。
在y的颜色是BLACK的情况下,如果x的颜色为RED的话,删除y之后,结点y所在的分支的黑高就会减1,所以,只需要将x的颜色变为BLACK,则该分支的黑高会加1,则会保持住红黑树的颜色性质。
所以最终要考虑的情况就是,y颜色为BLACK,x的颜色为BLACK的情况。因把y删除后,x顶替y的位置,y所在分支的黑高减1,所以,假设x节点的颜色为BLACK-BLACK,简称BB,也就是原来y的BLACK增加到x上了,这样就保证了该分支的黑高不变,接下来要做的就是调整x所在的分支,使红黑树的性质保持不变,又分为下面的几种情况(只考虑x为左孩子的情况,右孩子的情况是对称的):
对于下面四种情况之间的转换,需要保证转换前和转换后,红黑树的性质都得到了维持。
1:x的兄弟结点w的颜色为RED,记为case1。
因为w为RED,所以x.p的颜色为BLACK,w的两个孩子的颜色都为BLACK,如下图:
转换前,满足下面的性质:
a:bh(p) =bh(x) + 2 = bh(w) = bh(1) + 1 = bh(2) + 1
b:整个分支的黑高为bh(p)+ 1 (p.color == BLACK) = bh(x) + 3
对于这种情况,需要做的调整的代码如下:
w.color =BLACK // case1
x.p.color =RED // case1
LEFT-ROTATE(T,x.p) // case 1
w = x.p.right // case1
经过w.color = BLACK和x.p.color = RED的调整后,如下图:
对结点p进行左旋:LEFT-ROTATE(T, x.p),左旋之后,得到下面的图:
调整后,满足下面的性质:
a:bh(x), bh(1), bh(2)的值保持不变
b:因调整前有:bh(x)+ 2 = bh(1) + 1 = bh(2) + 1
所以,bh(p) = bh(x) + 2 = bh(1) + 1,所以p结点为根的子树满足红黑树性质;
bh(w) = bh(2) + 1 = bh(p),所以w结点为根的子树满足红黑树性质
c:整个分支的黑高为bh(w)+ 1 (w.color == BLACK) = bh(x) + 3,所以,整个分支的黑高没变
这时,x的兄弟的颜色为BLACK,所以经过w = x.p.right后,得到了另外一种情况;
这种情况下,要根据w的孩子结点的颜色不同分为三种情况:
2:首先是w的孩子的颜色都是BLACK的情况下,记为case2:
转换前,满足下面的性质:
a:bh(p) =bh(x) + 2 = bh(1) + 2 = bh(2) + 2 = bh(w)+1
b:整个分支的黑高为bh(p)+ 1/0(p.color == BLACK?1:0) = bh(x) + 3/2(p.color == BLACK?3:2)
对于这种情况,需要调整的代码为:
w.color = RED // case 2
x= x.p //case 2
调整后,得到下面的图:
调整后,满足下面的性质:
a:bh(x), bh(1), bh(2)的值保持不变
b:因调整前有:bh(x)+ 2 = bh(1) + 2 = bh(2) + 2
所以,bh( )= bh(x) + 1 = bh(1) + 1 = bh(2) + 1, 结点为根的子树满足红黑树性质; bh(w) = bh(1) + 1 = bh(2) + 1, w结点为根的子树满足红黑树性质
如果p,也就是 的颜色为RED,则退出循环,并且置 颜色为BLACK。
c:整个分支的黑高为bh( )+ 1 ( .color== BLACK) = bh(x) + 2,所以,整个分支的黑高没变。
如果p的颜色为BLACK,则 的颜色为BLACK-BLACK。
c:整个分支的黑高为bh( )+ 2 ( .color== BB) = bh(x) + 3,所以,整个分支的黑高没变。
这样, 之下的结点维持了红黑树的性质,然后以 为新的结点继续循环处理。
3:如果w的右孩子结点为RED,左节点颜色为任意,case3:
转换前,满足下面的性质:
a:bh(p) =bh(x) + 2 = bh(2) + 1 = bh(1) + 2/1(1.color == BLACK?2:1)
b:整个分支的黑高为bh(p)+ 1/0(p.color == BLACK?1:0) = bh(x) + 3/2(p.color == BLACK?3:2)
对于这种情况,需要调整的代码为:
w.color = x.p.color // case 3
x.p.color= BLACK //case 3
w.right.color= BLACK //case 3
LEFT-ROTATE(T,x.p) // case3
x = T.root //case 3
经过w.color = x.p.color; x.p.color = BLACK;w.right.color = BLACK之后,得到下面的图:
经过LEFT-ROTATE(T, x.p)和x = T.root之后,得到下图:
调整后,满足下面的性质:
a:bh(x), bh(1), bh(2)的值保持不变
b:因调整前有:bh(x)+ 2 = bh(2) + 1 = bh(1) + 2/1(1.color == BLACK?2:1)
所以,bh(w) = bh(x) + 2 = bh(2) + 1 = bh(1) +2/1(1.color == BLACK?2:1),w结点为根的子树满足红黑树性质; bh(p) = bh(x) + 1 = bh(1) + 1/0(1.color == BLACK?1:0), w结点为根的子树满足红黑树性质
c:整个分支的黑高为bh(w) + 1/0(p.color == BLACK?1:0)= bh(x) = 3/2(p.color == BLACK?3:2)。所以整个分支的黑高没变。
4:如果w的右孩子结点为BLACK,左节点颜色为RED,case4:
转换前,满足下面的性质:
a:bh(p) =bh(x) + 2 = bh(2) + 2 = bh(1) + 1 = bh(3) + 2 = bh(4) + 2
b:整个分支的黑高为bh(p)+ 1/0(p.color == BLACK?1:0) = bh(x) +3/2(p.color == BLACK?3:2)
对于这种情况,需要调整的代码为:
w.left.color= BLACK // case 4
w.color= RED //case 4
RIGHT-ROTATE(T,w) // case 4
w= x.p.right //case 4
经过w.left.color = BLACK和w.color = RED的调整后,得到下面的图:
经过RIGHT-ROTATE(T.w)和w = x.p.right的调整之后,得到下面的图:
调整后,满足下面的性质:
a:bh(x), bh(3), bh(4), bh(2)的值保持不变
b:因调整前有bh(x)+ 2 = bh(2) + 2 = bh(3) + 2 = bh(4) + 2,所以:
bh(w)= bh(4) + 1 = b(2) + 1,所以,w为根节点的子树红黑树性质没变;
bh( )= bh(3) + 1 = bh(4) + 1 = b(2) + 1,所以, 为根节点的子树红黑树性质没变;
bh(p)= bh(x) +2 = bh(3) + 2 = bh(4) + 2 = b(2) + 2,所以,p为根节点的子树红黑树性质没变;
c:整个分支的黑高为bh(p)+ 1/0(p.color == BLACK?1:0) = bh(x) +3/2(p.color == BLACK?3:2),所以整个分支的黑高。
同时注意到,新的w( )颜色为BLACK,而其右孩子为RED,符合case3。
分析:
n个结点的红黑树高度为O(lg n),不调用RB-DELETE-FIXUP时,时间代价为O(lg n),情况1可以转换为情况2;也可以转换为情况3,然后退出循环;也可以转换为情况4,然后情况4可以转换为情况3,情况3只执行一次循环。情况2是唯一能在while循环中执行多次的情况,所以RB-DELETE-FIXUP的时间复杂度为O(lg n)。所以,红黑树的删除的时间复杂度为O(lg n)。