红黑树-删除操作

从一颗红黑树中删除结点的过程和二叉搜索叔的过程类似。假设删除结点为p,f为其父结点。

二叉搜索树(BST)删除结点

  • 若p结点为叶子结点,直接删除即可。
  • p结点只有左子树或只有右子树,此时只需要将p删掉,然后将p的子树直接连在原来p的双亲结点f上。
  • p既有左子树又有右子树。先沿着p的左子树根结点的右指针一直往右走,直到来到其右子树的最右边的一个结点r(也可以沿着p的右子树根结点的左指针一直往左走,直到来到其左子树的最左边的一个结点)。然后将p中的关键字用r中关键字代替。最后判断,如果r是叶子结点,则按照第一种情况进行处理;如果r是非叶子结点,则按照第二种情况处理。此时的r不可能是有两个子树的结点。

红黑树删除结点

  • 首先按照二叉搜索树从红黑树中删除结点,然后通过改变颜色和执行旋转来恢复红黑树的性质。
  • 设变量 y 为从树中删除的结点或者为移至树内的结点。当待删除结点z 的子结点少于2个时,y 为从树中删除的结点;当z 有两个子结点时,y 指向z 的后继。
  • 由于结点y 的颜色有可能改变,因此需要存储改变前y的颜色。如果结点y 是黑色的,就有可能引入一个或多个红黑树的性质被破坏的情况。如果y是红色的,当y被删除或者移动后,红黑树性质仍然保持,
    • 树中的黑高没有发生变化。
    • 不存在两个相邻的红结点。
    • 如果待删除结点z只有一颗子树或者没有子树(即叶结点),此时y指向z(即y指向从树中删除的结点),若y为红色,则y若有子结点,必为黑色,则红黑树的性质1,2,3,4,5都不会违反;
    • 如果z有两棵子树,此时如果y不是z的右孩子,则y的原右孩子结点x将代替y,如果y是红色,则x一定是黑色的,因此用x代替y不可能使两个红结点相邻。如果y是z的右孩子,则用y代替z之后,需要用z的颜色覆盖掉y原先的颜色(即y无论为什么颜色,现在都为z的颜色-黑色),红黑树的性质也不会遭到破坏。
    • 如果y是红色的,就不能是根结点,所以根结点仍旧是黑色的。
  • 如果y为黑色则会产生一下问题,
    • 如果结点y为根结点,则y的一个红色结点成为新的根结点。
    • 如果x和x.p(x结点需要调整到y的原先的位置)是红色的, 则违反红黑树的性质4。
    • 在树中移动y将导致先前包含y的任何简单路径上黑结点个数少1,违反性质5。

红黑树删除之后的修复

  • 修复红黑树的办法是将现在占有y原来位置的结点x视为还有一重额外的黑色,,也就是说,如果将任意包含结点x的简单路径上黑结点个数加1,则在这种情况下,性质5成立。当将黑结点y删除或者移动时,将其黑色”下推”给结点x。那么现在的问题变为结点x即不是红色也不是黑色,从而违反性质1。现在的结点x是双重黑色或者红黑色。
  • 修复红黑树的总目标是将额外的黑色沿树上移。直到x指向红黑结点或者x指向根结点,那么此时直接将x(红黑结点)着为黑色;执行适当的旋转和重新着色,退出循环。

设x为代替y位置的结点,x的兄弟结点为w,以下的讨论都是以y结点为黑色进行的。

如果x是红色的,则会破坏性质5。
  • 对策:直接把x结点着为红色,红黑树的性质恢复。
如果当前结点是黑色且为根结点。
  • 则什么也不做结束。。
情况1:x的兄弟结点w是红色的
  • 对策:把兄弟结点w着为黑色,父结点着为红色,然后对父结点做一次左旋。
情况2:x的兄弟结点w是黑色的,且w的两个子结点都是黑色的
  • 对策:把当前结点和兄弟结点去掉一重黑色(把兄弟结点变为红色),把父结点当成新的当前结点。
情况3:x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的。
  • 对策:把兄弟结点着为红色,兄弟结点的左子着为黑色,之后以兄弟结点为支点进行右旋。
情况4:x的兄弟结点w为黑色,且w的右孩子是红色的。
  • 对策:把兄弟结点着为当前结点的父结点的颜色,把父结点和兄弟结点的右子结点着为黑色,然后以当前结点的父结点为支点左旋。

实现代码

public void deleteRBTNode(RBTNode z){
        //y始终指向从树中删除的结点或者移至树内的结点。
        //x为需要需要移至到y位置的结点
        RBTNode y=z,x;
        Color yOriginalColor=y.color;
        if(z.leftChild==null){ //z只有右子树或者y为叶子节点
            x=y.rightChild;
            transplant(y,x);
        }
        else if(z.rightChild==null){ //z只有左子树
            x=y.leftChild;
            transplant(y,x);
        }
        else //z既有左子树又有右子树的情况
        {
            y=treeMinimum(z.rightChild);//y指向移至树内的结点
            yOriginalColor=y.color;
            x=y.rightChild;
            if( y.parent==z){ //z的后继结点为为其右孩子(y)
                if(x!=null)//y的右孩子(x)不为空的时候,
                    x.parent=y;//x需要占据y的位置
            }
            else
            {
                transplant(y,x);
                y.rightChild=z.rightChild;
                y.rightChild.parent=y;
            }
            transplant(z,y);
            y.leftChild=z.leftChild;
            y.leftChild.parent=y;
            y.color=z.color;
        }
        if(yOriginalColor==Color.BLACK && x!=null)
            deleteFixUp(x);

    }
//寻找以node为根结点的子树上值最小的节点
    public RBTNode treeMinimum(RBTNode node){
        while(node.rightChild!=null)
            node=node.leftChild;
        return node;
    }
//寻找以node为根结点的子树上值最大的结点
public RBTNode treeMaximum(RBTNode node){
        while(node.rightChild!=null)
            node=node.rightChild;
        return node;
}
//删除红黑树结点之后的修复操作
private void deleteFixUp(RBTNode x){
        while(x!=root && x.color==Color.BLACK){
            if(x==x.parent.leftChild){ //x为左孩子的时候
                RBTNode w=x.parent.rightChild;//w指向x的(右)兄弟结点
                if(w.color==Color.RED){//兄弟结点为红色的时,此时父结点和兄弟结点的孩子结点为必为黑色
                    w.color=Color.BLACK;//将兄弟结点由红色着为黑色
                    x.parent.color=Color.RED;//将父结点右黑色着为红色
                    leftRotation(x.parent);//以父结点为支点进行旋转
                    w=x.parent.rightChild;//重新指向新的兄弟结点
                }

                if(  w.leftChild!=null && w.leftChild.color==Color.BLACK &&
                        w.rightChild!=null && w.rightChild.color==Color.BLACK){//兄弟结点的子节点全为黑色
                    w.color=Color.RED;
                    x=x.parent;
                }
                else{
                    if( w.rightChild!=null && w.rightChild.color==Color.BLACK){//左孩子为红色,右孩子为黑色
                        w.color=Color.RED;//兄弟结点由黑色着为红色
                        w.leftChild.color=Color.BLACK;//兄弟结点的左孩子结点由红色着为黑色
                        rightRotation(w);
                        w=x.parent.rightChild;
                    }
                    //x的兄弟结点(W)为黑色,且兄弟结点(W)的右节点为红色的,左节点颜色任意
                    w.color=w.parent.color;
                    w.rightChild.color=Color.BLACK;
                    w.parent.color=Color.BLACK;
                    leftRotation(w.parent);
                    x=this.root;
                }
            }
            else{//x为右孩子的时候
                RBTNode w=x.parent.leftChild;//w指向x的(右)兄弟结点
                if(w.color==Color.RED){//兄弟结点为红色的时,此时父结点和兄弟结点的孩子结点为必为黑色
                    w.color=Color.BLACK;//将兄弟结点由红色着为黑色
                    x.parent.color=Color.RED;//将父结点右黑色着为红色
                    rightRotation(x.parent);//以父结点为支点进行旋转
                    w=x.parent.leftChild;//重新指向新的兄弟结点
                }

                if(  w.leftChild!=null && w.leftChild.color==Color.BLACK &&
                        w.rightChild!=null && w.rightChild.color==Color.BLACK){//兄弟结点的子节点全为黑色
                    w.color=Color.RED;
                    x=x.parent;
                }
                else{
                    if( w.leftChild!=null && w.leftChild.color==Color.BLACK){//左孩子为红色,右孩子为黑色
                        w.color=Color.RED;//兄弟结点由黑色着为红色
                        w.rightChild.color=Color.BLACK;//兄弟结点的左孩子结点由红色着为黑色
                        leftRotation(w);
                        w=x.parent.leftChild;
                    }
                    //x的兄弟结点(W)为黑色,且兄弟结点(W)的右节点为红色的,左节点颜色任意
                    w.color=w.parent.color;
                    w.leftChild.color=Color.BLACK;
                    w.parent.color=Color.BLACK;
                    rightRotation(w.parent);
                    x=this.root;
                }
            }
        }
        x.color=Color.BLACK;
    }

本文参考:《算法导论》,红黑树实现

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值