算法学习之数据结构之红黑树(二)

  红黑树的删除。

  红黑树的删除是在二叉查找树的基础上修改得来的, 从红黑树上删除一个节点,可以先用普通二叉搜索树的方法,将节点从红黑树上删除掉,然后再将被破坏的红黑性质进行恢复。
  我们回忆一下普通二叉树的节点删除方法: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的一层黑色去掉就行了(因为根节点被包含在所有的路径上,所以这样做所有路径同时黑高减少一,不会破坏红黑特征)。
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循环结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值