算法导论学习--红黑树详解之删除(含完整红黑树代码)

前面我们讨论了红黑树的插入的实现,基本思想是分类讨论;然后分情况讨论以后我们发现插入操作调整函数只需要处理三种情况,并不是太复杂。但是删除操作会更复杂一点,因为二叉搜索树的删除操作本身就分成了多种情况,这样在执行删除操作后要处理的情况会更多;下面对于删除操作我们仍旧采取分类讨论的方法,将所有的情况梳理清楚后,就可以理解红黑树的删除调整函数的实质了。
下面先放出红黑树删除函数的代码:

//红黑树删除函数
///类似于二叉树删除函数,不过在删除完成以后需要调用调整函数恢复性质
///总的过程也是按z的左右儿子情况进行分类.
///1.z只有左儿子/右儿子
///2.z有左右儿子,z的后继结点是z的右儿子
///3.z有左右儿子,z的后继结点不是z的右儿子
void RBDelete(RBTree z)
{ ///在下面的过程中y总是指向树中会被删除的结点或是会被替代的结点
   ///x总是指向要替代z或y的结点

   RBTree x=NULL;
   RBTree y=z;
   int ycolor=y->color; ///记录y原来的颜色
   if(z->left==Nul) ///只有右儿子
   {
       x=z->right;
       RBTransplant(z,z->right);
   }
   else if(z->right==Nul) ///只有左儿子
   {
       x=z->left;
       RBTransplant(z,z->left);
   }
   else  ///左右儿子都有
   {
       y=RBTreeMinMuM(z->right); ///查找z的后继
       ycolor=y->color;
       x=y->right; ///因为后面y会被染成z原来的颜色,所以违反性质的就是y的右儿子
       if(y->p==z) ///y是z的孩子结点
           x->p=y;///这种情况下,y为x的父结点
       else ///y不是z的孩子结点的情况
       {
  ///先用y的右孩子取代y,然后再更改z的右孩子的指向
           RBTransplant(y,y->right);
           y->right=z->right;
           y->right->p=y;
        }
       RBTransplant(z,y); ///y取代z
       y->left=z->left; ///z的左孩子改变指向
       y->left->p=y;
       y->color=z->color; ///更改y的颜色,这样的话从y以上红黑树的性质都不会违反
   }
   ///如果y原来的颜色是黑色,那么就意味着有一个黑色结点被覆盖了,
   ///红黑树的性质可能会被破坏(性质4或5),需要调整
   if(ycolor==BLACK)
      RBDeleteFixUp(x);
}

我们设要删除的结点为z,在下面的过程中y总是指向树中会被删除的结点或是会被替代的结点而x总是指向要替代z或y的结点;从代码上看,红黑树的删除操作与二叉树搜索树一样都是分三种情况进行的:

1).z只有左儿子/右儿子
2).z有左右儿子,z的后继结点是z的右儿子
3).z有左右儿子,z的后继的结点不是z的右儿子
那么现在我们就来分析这三种情况下删除z需要怎样操作,以及删除z后红黑树的那些性质会被违反,我们如何进行调整以恢复性质。

情况1:z只有左儿子/右儿子
这种情况下删除操作是很简单的,我们只需要用z的左孩子或右孩子替代z就行了;然后y表示z,x表示z的左孩子或右孩子,ycolor表示z的颜色。
(1).如果z的颜色为红;那么这种替代以后红黑树不会有任何性质被违反,所以就不需要进行调整。
(2).如果z的颜色为黑;那么就意味着有一个黑色结点被覆盖了,这时如果替代z的x是黑色,那么就意味着黑高减少,性质5被破坏了,这时我们可以在x上再人为的”增添”一重黑色,此时x变成双重黑色的结点,但是性质5恢复了;如果x是红色,那么如果z的父结点是红色的,那么性质4和性质5都就会被破坏,这时我们就可以将x染成红色,这样性质4,性质5都可以恢复了。

情况2:z有左右儿子,并且其右儿子就是其后继结点
我们知道一个结点的后继就是将所有结点按关键字从小到大的排序后,排在其后面的那一个结点。z的右儿子就是z的后继,说明z的右儿子的左子树为空。此时y表示z的右儿子,而x表示y的右儿子。删除过程应该是用x替代y,然后就用y替代z,并且将y染成z原来的颜色,ycolor表示y原来的颜色。现在因为y替代z以后被染成z原来的颜色,所以至z以上红黑树的所有性质都不会变,唯一有可能会影响红黑树性质的地方在x替代y这一点。所以其实情况有返回到情况1了,下面按y的颜色进行分类讨论:
(1).y的颜色为红;那么无论x的颜色为红还是为黑,x替换y以后都不会影响任何性质。
(2).y的颜色为黑;这时与上面的情况1是相似的,如果x为黑,则性质5会被违反,我们通过将其染成双重黑色解决。如果x为红,则性质4,5都会被违反,但是我们可以通过将x染成黑色恢复。

情况3:z有左右儿子,并且z的后继不是其右儿子
其实这种情况和情况2是基本一样的,唯一不同的点在于y的位置会变。在情况2中y是z的右儿子,而在这里我们需要用一个查找后继的函数查找到y,然后x仍然表示y的右儿子,ycolor表示y的颜色。同样的x取代y,然后y取代z并被染成z原来的颜色。所以我们还是只需要关注x取代y的过程。接下来按y的颜色讨论,其实与上面情况2是一模一样的:
(1).如果y的颜色为红色;那么无论x为红还是为黑,替换都不会影响任何性质,无需调整。
(2).y的颜色为黑;如果x为黑,则性质5会被违反,我们通过将其染成双重黑色解决。如果x为红,则性质4,5都会被违反,但是我们可以通过将x染成黑色恢复。

上面的所有情况讨论完了以后,我们就发现如果ycolor为红,则删除以后不需要任何的调整。否则如果ycolor为黑,红黑树的性质就会被违反,需要进入调整函数中调整恢复性质。并且进入调整函数时,如果x为红,那么我们简单的将其染成黑色就可以恢复性质了;如果x为黑,那么进入调整函数是我们就将其看成带有双重黑色的情况,调整函数中需要消除那重额外的黑色。对于x为双重黑色的情况,还有一点需要注意:如果此时x为根结点,我们可以简单的去掉一重黑色就行了(其实就是不需要做任何操作),这时黑高是不会受影响的。所以我们下面集中精力来讨论如何调整双重黑色的情况。

(1).w的颜色为红
此时x的父结点一定是黑色的,我们可以通过将w染成黑色,x的父结点染成红色,然后对x的父结点进行左旋变成下面w为黑的情况(旋转以后要重新指定w)。

(2).w的颜色为黑
此时又需要按照w的左右儿子的颜色进行分类讨论
1).w的左右儿子都为黑色:此时我们可以将w染成红色,x移动为x的父结点,将x在树中上移一层,如果x->p是根结点或x->p原来是红色则结束循环,否则转成情况(1).
2).w的右儿子为黑(左孩子为红):此时我们可以通过染色和选择转成w的右孩子为红的情况(具体的操作见代码)
3).w的右儿子为红:这种情况下,我们是可以通过选择和染色去掉x的双重黑色,结束循环的(具体操作见代码)

至此我们就将调整函数中所有的情况讨论完了,下面给出调整函数的代码:

///红黑树删除调整函数
///这个函数主要要解决的问题是x被染成红黑色,或是双重黑色的问题
///对于第一种情况只要简单的去掉x的红色就行了。
///对于第二种情况我们分情况讨论,将双重黑色的结点在树中上升
///直到转成情况1,或是上升为根结点
void RBDeleteFixUp(RBTree x)
{
    while(x!=rt&&x->color==BLACK)
    {
        if(x==x->p->left)///按x是其父结点的左/右孩子分情况讨论
        {
  ///下面的过程要按其兄弟结点的颜色进行分类讨论
            RBTree w=x->p->right; ///其兄弟结点

            ///Case 1
            if(w->color==RED)///如果兄弟结点是红色
            {
  ///此时父结点一定是黑色;在保证黑高的情况下
            ///我们通过染色和旋转转成下面兄弟结点为黑色的情况

                w->color=BLACK;
                x->p->color=RED;
                LeftRotate(x->p);
                w=x->p->right;
            }

            ///Case 2
            if
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值