红黑树的删除

回忆一下红黑树中之前的内容:

一、基本性质:红黑性质。

       1每个结点或是红色,或是黑色。

       2、根节点是黑色的。

       3、每个叶节点(NIL)为黑色。

       4、若一个结点是红色的,则其两个子节点都为黑色。

       5、对每个结点,从该节点到所有后代叶节点的简单路径上,均包含相同数目的黑色结点。

二、之后外面学习了红黑树的旋转:用来调整指针结构。

Right_Rotate(T, y)
  x = y.left;
  y.left = x.right;
  if x,right != NIL
     x.right.p = y;
  x.p = y.p;
  if y.p == NIL
     T.root = x;
  else if y == y.p.left;
     y.p.left = x;
  else y.p.right = x;
  x.right = y;
  y.p = x;

三、再之后,我们通过将新插入的结点z标记为红色再将其进行插入,之后我们在z.p为红色,即违反性质2或者性质4时,分析叔结点的颜色,完成不同情况的转化,从而保证z.p.p这棵小子树满足红黑性质4、5,并在最后调剂根节点为红色,从而完成Insert例程。

RB_Insert(T, z)
{
 y = T.NIL;
 x = T.root;
 while x!=NIL
       y = x;
       if z.key < x.key
          x = x.left;
       else x = x.right;
 z.p = y;
 if y == T.NIL
    T.root = z;
 elseif z.key < y.key
        y.left = z;
 z.left = T.NIL;
 z.right = T.NIL;
 z.color = RED;
 RB_Insert_FixUp(T, z);
RB_Insert_FixUp(T, z)
 while z.p.color == RED
       if z.p == z.p.p.left
          y = z.p.p.right;
          if y.color == RED
             z.p.color = BLACK;
             y.color = BLACK;
             z.p.p.color = RED;
             z = z.p.p;
          if z == z.p.right
             z = z.p;
             Left_Rotate(T, z); //旋转z的父亲结点
             z.p.color = BLACK;
             z.p.p.color = RED;
             Right_Rotate(T,z.p.p)
       else y = z.p.p.right;
           if y.color == RED
              z.p.color = BLACK;
              y = BLACK;
              z.p.p.color = RED;
              z = z.p.p;
           if z == z.p.left
              z = z.p;
              Right_Rotate(T, z); 
              z.p.color = BLACK; //注意此时的z.p是最初的z.p.p,这时最初的叔结点为黑色
                                 //同时,此时新的z结点已经转换到了z.p的位置,因此设置的是
                                 //最初的z的z.p.p.p的颜色
              z.p.p.color = RED
              Left_Rotate(T,z.p.p);
 T.root.color = BLACK;

 现在,我们学习红黑树的delete例程。

四、删除。

     删除结点需要花费O(lg n)的时间。

     从一棵红黑树删除结点的过程基于搜索树的Tree_Delete过程,现在我们对其进行一波复习。

     为实现Tree_Delete过程,我们先实现了一个Transplant子过程,是用另一棵子树替换一棵子树并称为其父亲的子节点。

Transplant(T, u, v) //只做到了父节点的改变,真·移植
if u.p == NIL
   T.root = v
else if u == u.p.left
        u.p.left = v;
else u.p.right = v;
if v != NIL
   v.p = u.p;

Tree_Delete(T, z)
if z.left == NIL
   Transplant(T, z, z.right);
else if z.right == NIL
   Transplant(T, z, z.left);
else y = Tree_Minimum(z.right); //在z的右子树中获得最小的结点,即最左边的结点,它一定没有左儿子
   if y.p != z                   //当y.p = z时,y就是p的右儿子
     Transplant(T, y, y.right);  //因为y要被移走,因此用y.right代替y
     y.right = z.right;          //修改z结点的右儿子和y.right
     y.right.p = y;
   Transplant(T, z, y);
   y.left = z.left;              //修改z结点左儿子的p和y.left
   y.left.p = y;

  现在设计红黑树的Transplant过程:大体上没什么区别,不过因为现在只有一个哨兵结点,因此用T.NIL来代替NIL。

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; //这行也不同,因为当v = T.NIL时也能对v.p进行赋值(书上写的,原理不清楚)

过程Rb_Delete与Tree_Delete也类似,不过需要追踪y的踪迹:y有可能导致红黑性质被破坏。

Rb_Delete(T, z)
y = z;
yOriginalColor = y.color;
if z.left == T.NIL
   x = z.right;
   RB_Transplant(T, z, z.right);
elseif z.right == T.NIL
       x = z.left;
       RB_Transplant(T, z, z.left);
else   y = Tree_Minimum(z, right);
       yOriginalColor = 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 yOriginalColor == BLACK
          RB_Delete_FixUp(T, x);

最为重要的问题在于,当y是红色的,当y被删除或是移动,红黑性质仍然保持。

原因如下:

1、树中的黑高没有变化。(一条路径上黑色结点的数目)

2、不存在两个相邻的红色结点:因为y在树中占据了z的位置再考虑到y的颜色:z是红色时:z的儿子和父亲必然为黑色,此时y为红色,那么y必然不可能是z的右儿子,此时我们仅需要把y的右儿子提到y的位置:由于y没有左儿子,考虑到原来的红黑树符合红黑性质5,因此从y结点出发到其叶节点的黑高应该相同,因此,在红黑树中,y也没有右儿子,即替换到y的地方的结点成了T.NIL,之后在删除z的后,为了维持性质5,即保持黑高不变,我们把y染成z的颜色,然而由于y和z都为红色,因此并不会产生影响;而z是黑色时,z的父亲和儿子的颜色尚未确定:而y的左右儿子同样必为T.NIL,因此因此将右儿子移动到y处,那么y的左儿子也将消失——NIL结点不会再拥有儿子。

总之,当y是红色时,红黑性质仍然保持(虽然我并不认同书上的部分观点)

而当y为黑色,将会产生三个问题:

1、若y是原来的根节点,而y的一个红色孩子称为新的根节点,就违反了性质2。(注意:y在这里不一定是z的右子树的最小值,还有可能是z本身)

2、若x和x.p均为红色,则违反性质4。(此处的x.p是原来的y.p)

3、在树中移动y将导致先前包含y的任何简单路径上的黑节点少1,因此,y的任何祖先都不满足性质5。改正这一问题的方法是将现在占有y原来位置的结点x视为还有一重额外的黑色,即,如果把含有x结点的简单路径上黑节点的个数+1,则性质5将重新成立。而这个方法将破坏性质1:把y的黑色下推给了x,那么x将具有红黑色或者双重黑色。因此,额外的黑色是针对x结点而非x.color属性上。

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
              x.p.color = RED
              Left_Rotate(T, x.p)
              w = x.p.right
           if w.left.color == BLACK and w.right.color == BLACK
              w.color = RED
              x = x.p
           else if w.right.color == BLACK
                   w.left.color = BLACK
                   w.color = RED
                   Right_Rotate(T, w)
                   w = x.p.right
              w.color = x.p.color
              x.p.color = BLACK
              w.right.color = BLACK
              Left_Rotate(T, x.p)
              x = T.root
        else (same as then clause with "right" and "left" exchange)
x.color = BLACK

过程RB_Delete_FixUp恢复性质1、2以及4。

while循环的目的是将额外的黑色沿树上移,(注:上移的并非结点,而是指向结点的指针x,由于此处并不是->而是.,因此可以将x也看作为一个引用,该引用含有一个额外的黑色)直到:

1、 x 指向红黑结点;

2、x指向根节点;

3、执行适当的旋转、重新着色以退出循环。

之后分析代码:

在while循环中,x总是一个具有双重黑色的非根结点。而if ( x == x.p.left) 则判断x是左孩子还是右孩子;同时保持w指向x的兄弟。由于x为双重黑色,因此w不可能为T.NIL——否则从x.p到x和到w的黑结点个数必然不同。

 

在之后的代码中,通过w结点,以及w的子代的结点的颜色分情况进行讨论。

 

1、x的兄弟节点w为红色。

     此时,由于w为红色,那么其两个子节点必然为黑色,此时可以改变w和x.p的颜色(因为x本身是黑色,因此改变w后,也可改变父节点x.p的颜色),之后对x.p进行一次左旋转,此时仍满足红黑性质5,而同时w便成了x.p.p,而x现在的兄弟结点则为w的right子节点,其为黑色,因此就能转换为其他的情况。

2、x的兄弟结点w为黑色,而w的两个子节点均为黑色。

   在这种情况下,x、w均为黑色,因此,去掉x和w上的一重黑色,使得x只有一重黑色,而w变为红色,而为将x.p增加一层黑色。并此时将x.p作为x结点进行循环。

3、x的兄弟结点w为黑色,w的左孩子是红色,而w的右孩子是黑色。

  此时交换w和w.left的颜色,之后w为红,w.left为黑,同时对w进行右旋,便可以使得w.left到了上面,而w成了w原本左儿子的右儿子结点,且为红色,这便进入到了第四中情况。

4、x的兄弟结点w为黑色,且w的右孩子是红色

  此时改变w结点的颜色,使之与x.p相同,再将x.p的颜色染为黑色,同时将w右边结点变为黑色,再对x.p进行一次左旋,便可以去掉x额外的黑色:当x.p是黑色时,此时从x.p到x共有3层黑色,我们选择将w右边的变为黑色,此时再将w旋转到上面来,而此时x.p成了w的儿子,而从w到x仍有3层黑色,因此w的右孩子便是我们为之将红色转换为黑色的点;而当x.p是红色时,我们将w变为红色,而此时x.p变为黑色,此时红黑结点在w存在的路径的个数不变,同时依然将w右边变为黑色,即可。当x抵达根节点时,循环终止。

 

这就是删除的所有细节。

 

   

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值