红黑树的删除。
红黑树的删除是在二叉查找树的基础上修改得来的,
从红黑树上删除一个节点,可以先用普通二叉搜索树的方法,将节点从红黑树上删除掉,然后再将被破坏的红黑性质进行恢复。
我们回忆一下普通二叉树的节点删除方法:Z指向需要删除的节点,Y指向实质结构上被删除的结点,如果Z节点只有一个子节点或没有子节点,那么Y就是指向Z指向的节点。如果Z节点有两个子节点,那么Y指向Z节点的后继节点(其实前趋也是一样的),而Z的后继节点绝对不可能有左子树。因此,仅从结构来看,二叉树上实质被删除的节点最多只可能有一个子树。
伪代码如下:
rb-delete(T, z)
if left[z] == nil[T] or right[z] == nil[T]
then y = z //如果Z节点只有一个子节点或没有子节点,那么Y就是指向Z指向的节点。
else y = tree-successor(z) // Y指向Z节点的后继节点
if left[y] != nil[T] // y是x的子节点
x = left[y]
else y = right[y]
parent[x] = parent[y] // 直接将x的父节点置为y的父节点,即x的祖先节点
if parent[y] == nil[T] // 根节点为空
then root[T] = x
else if y = left[parent[y]] // y是其父节点的左孩子节点
then left[parent[y]] = x
else right[parent[y]] =x
if y != z
then key[z] = key[y]
if color[y] == black // 如果y是黑色的,则进行修补以保持红黑树的性质
rb-delete-fixup(T, x)
return y
如果y是红色的,删除后则红黑性质依然可以得到保持,理由如下:
1,树中的各节点的黑高度没有变化,
2,不存在两个相邻的红节点,
3,如果y是红色的,则不可能是根,根仍然是红色的。
如果Y指向的节点是个黑色节点,那么就有几条红黑性质可能受到破坏了。首先是包含Y节点的所有路径,黑高度都减少了一(第5条被破坏)。其次,如果Y的有红色子节点,Y又有红色的父节点,那么Y被删除后,就出现了两个相邻的红色节点(第4条被破坏)。最后,如果Y指向的是根节点,而Y的子节点又是红色的,那么Y被删除后,根节点就变成红色的了(第2条被破坏)。
如果不改变含Y路径的黑高度,那么树的其它部分的黑高度就必须做出相应的变化来适应它。所以,我们想办法恢复原来含Y节点的路径的黑高度。做法就是把Y节点的黑色,推到它的子节点X上去。(X可能是NIL节点)。这样,X就可能具有双重黑色,或是同时具有红黑两色,也就是第1条性质被破坏了。
但第1条性质是比较容易恢复的:一、如果X是同时具有红黑两色,那么好办,直接把X涂成黑色,就行了。而且这样把所有问题都解决了。因为将X变为黑色,2、4两条如果有问题的话也会得到恢复。二、如果X是双黑色,那么我们希望把这种情况向上推一直推到根节点(调整树结构和颜色,X的指向新的双黑色节点,X不断向上移动),让根节点具双黑色,这时,直接把X的一层黑色去掉就行了(因为根节点被包含在所有的路径上,所以这样做所有路径同时黑高减少一,不会破坏红黑特征)。
但第1条性质是比较容易恢复的:一、如果X是同时具有红黑两色,那么好办,直接把X涂成黑色,就行了。而且这样把所有问题都解决了。因为将X变为黑色,2、4两条如果有问题的话也会得到恢复。二、如果X是双黑色,那么我们希望把这种情况向上推一直推到根节点(调整树结构和颜色,X的指向新的双黑色节点,X不断向上移动),让根节点具双黑色,这时,直接把X的一层黑色去掉就行了(因为根节点被包含在所有的路径上,所以这样做所有路径同时黑高减少一,不会破坏红黑特征)。
rb-delete-fixup(T, x)
while x != root[T] and color[x] == black
do if x == left[parent[x]] // 分为两组,两组是对称的,分别是x是左子树或右子树
then w = right[parent[x]] // w是其兄弟节点
if color[w] == red // w是红色,CASE1
then color[w] = black
color[parent[x]] = red
left-rotate(T, parent[x])
w = right[parent[x]]
if color[left[w]] == black and color[right[w]] == black // w是黑色的,两个孩子都是黑色的CASE2
then color[w] = red
x = parent[x] // x向上推
else if color[right[w]] = black // w是黑色的,左孩子红色的,右孩子是黑色的CASE3
then color[left[w]] = black
color[w] = red
right-rotate(T, w)
w = right[parent[x]]
// CASE4
color[w] = color[parent[x]]
color[parent[x]] = black
left-rotate(T, parent[x])
x = root[T]
else // x是右子树,进行类似的操作
same as then clause whith right and left exchanged
color[x] = black
简单分析下:
情况1:x的兄弟节点w是红色的
改变w和parent[x]的颜色,再对parent[x]做一次左旋转,并将w置为x新的兄弟节点,红黑性质得到保持,转到情况2,3,4.
情况2:x的兄弟节点w是黑色的,而且w的两个孩子都是黑色的。
因为w两个孩子都是黑色的,w也是黑色的,所以从x和w上去掉一重黑色,从而x只有一重黑色,w为红色,通过x为parent[x]及诶单来重复while循环,如果是从情况1进入情况2,新节点x的color为red则结束循环。
情况3:x的兄弟节点是黑色的,左孩子是红色的,右孩子是黑色的。
交换w和左孩子的颜色,并对w进行右旋转,从而红黑性质保持一致,这样情况3转为情况4。
情况4:x的兄弟节点w是黑色的,w的右孩子是红色的。
对颜色做修改,并对parent[x]做一次左旋,可以去掉x的额外黑色来把它变成单独黑色,而不破坏红黑性质。将x置为根后,while循环结束。