Java实现数据结构——红黑树删除

Posted x_k

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java实现数据结构——红黑树删除相关的知识,希望对你有一定的参考价值。

本文参考《算法导论(第三版)》和JDK1.8-HashMap源码

回顾红黑树基本性质

  1. 每个节点或是红色,或是黑色
  2. 根节点是黑色
  3. 每个叶节点是黑色
  4. 如果一个节点是红色,那么它的两个子节点都是黑色
  5. 对于每个节点,从该节点到其所有后代叶节点的简单路径上,包含相同数目的黑色节点

删除节点

节点的基本删除操作:二叉查找树-删除元素
《算法导论(第三版)》中的二叉查找树的删除操作原理是,使用删除节点的后继节点代替删除节点的位置

红黑树删除

获取双色节点(replacement)

由于Java实现红黑树中的性质3,每个叶节点都是Null,无法取得Null父节点指针,所以先使用删除节点作为双色节点,着色完毕后删除。
根据删除节点p的子节点作为不同情况处理:
1. p仅存在当个子节点,那么replacement设置为子节点。
2. p不存在子节点,那么replacement设置为p
3. p同时存在两个子节点,那么如果p的后继节点的右孩子sr不为空,那么replacement设置为sr,否则replacement设置为p

重新着色

当删除的节点为黑色节点时,删除操作会破坏性质5,需要重新着色。

replacement节点是其父节点的左孩子

  1. replacement的兄弟节点s是红色,左旋其父节点,交换其父节点和兄弟节点s颜色。
  2. 如果步骤1中执行操作后,replacement无兄弟节点s,则replacement向上传递。否则,执行步骤3。
  3. replacement的兄弟节点s不存在子节点,或存在两个黑色节点,那么s设为红色,向上传递replacement。否则执行步骤4。
  4. 兄弟节点s的右子节点不为红色的话,通过旋转设置为红色。
  5. 兄弟节点s的右子节点为红色。旋转replacement父节点,交换父节点和父节点的右子节点颜色,父节点设置为黑色,s的右子节点设为黑色。退出循环。
    步骤1例图:

    步骤3例图:

    步骤4例图:

    步骤5例图:

replacement节点是其父节点的右孩子

与上面基本一致

replacement为删除原节点

当删除的节点不存在子节点,或者删除节点的后继节点的右子节点不存在时,使用的是删除节点本身作为双色节点,重新着色后,需要断开删除节点与其父节点。

代码实现

public boolean delete(E e) 
        RBTreeNode<E> p, pl, pr, replacement;
        // 一、查找删除节点p
        if ((p = this.find(e)) == null) 
            return false;
        
        pl = p.left;
        pr = p.right;

        // 二、判断p的子节点
        if (pl != null && pr != null) 
            // 1、p存在两个子节点

            // 2、交换p和其后继节点s的位置和颜色

            // 2.1、获取后继节点s
            RBTreeNode<E> s = pr, sl;
            while ((sl = s.left) != null) 
                s = sl;
            

            // 2.2、交换p和s的颜色
            boolean c = s.red;
            s.red = p.red;
            p.red = c;

            // 2.3、s是否为p的右子节点
            RBTreeNode<E> sr = s.right, pp = p.parent;
            if (s == pr) 
                // 2.3.1、s为p的右子节点,s存在为p的父节点
                // 关联s节点的右子节点
                p.parent = s;
                s.right = p;
             else 
                // 2.3.2、s不为p的右子节点,s存在不为p的父节点
                // sp的左子节点关联p
                p.parent = s.parent;
                s.parent.left = p;
                // 关联s节点的右子节点
                s.right = pr;
                pr.parent = s;
            

            // 2.4、s的右子节点不为空,关联p
            if ((p.right = sr) != null) 
                sr.parent = p;
            

            // 2.5、关联s和pl,pp、清空p的左子节点指针
            s.left = pl;
            pl.parent = s;
            if ((s.parent = pp) == null) 
                root = s;
             else if (p == pp.left) 
                pp.left = s;
             else 
                pp.right = s;
            
            p.left = null;

            // 2.6、s节点是否存在右子节点
            if (sr != null) 
                // 存在,使用该节点作为替换节点,步骤三中,清空p
                replacement = sr;
             else 
                // 不存在,使用p作为替换节点
                replacement = p;
            
         else if (pl != null) 
            replacement = pl;
         else if (pr != null) 
            replacement = pr;
         else 
            // 删除节点为无子节点
            replacement = p;
        
        // 三、替换节点不为p时,删除p
        if (replacement != p) 
            // 删除节点存在子节点时,replacement替换p
            RBTreeNode<E> pp = replacement.parent = p.parent;
            if (pp == null) 
                root = replacement;
             else if (p == pp.left) 
                pp.left = replacement;
             else 
                pp.right = replacement;
            
            p.left = p.right = p.parent = null;
        
        // 四、重新着色
        if (!p.red) 
            root = this.balanceDeletion(this.root, replacement);
        
        // 五、分离
        if (replacement == p) 
            // 双色节点为p时,与其父节点断开
            RBTreeNode<E> pp = p.parent;
            p.parent = null;
            if (pp != null) 
                if (p == pp.left) 
                    pp.left = null;
                 else if (p == pp.right) 
                    pp.right = null;
                
            
        
        size--;
        return true;
    

重新着色

public <E> RBTreeNode<E> balanceDeletion(RBTreeNode<E> root, RBTreeNode<E> x) 
        for (RBTreeNode<E> xp, xpl, xpr; ; ) 
            if (x == null || x == root) 
                return root;
             else if ((xp = x.parent) == null) 
                x.red = false;
                return x;
             else if (x.red) 
                x.red = false;
                return root;
             else if ((xpl = xp.left) == x) 
                // x为左子节点
                if ((xpr = xp.right) != null && xpr.red) 
                    // x的兄弟节点存在,且是红色
                    xpr.red = false;
                    xp.red = true;
                    // 通过旋转将x的兄弟节点换成黑色节点
                    root = rotateLeft(root, xp);
                    xpr = (xp = x.parent) == null ? null : xp.right;
                
                if (xpr == null) 
                    // 无兄弟节点,x向上传递
                    x = xp;
                 else 
                    // 兄弟节点s的子节点分析
                    RBTreeNode<E> sl = xpr.left, sr = xpr.right;
                    if ((sr == null || !sr.red) && (sl == null || !sl.red)) 
                        // case 1:s不存在子节点
                        // case 2:s存在两个黑色子节点
                        xpr.red = true;
                        x = xp;
                     else 
                        if (sr == null || !sr.red) 
                            // s右孩子不存在或是黑色
                            // 通过旋转将s的右孩子换成红色
                            sl.red = false;
                            xpr.red = true;
                            root = rotateRight(root, xpr);
                            xpr = (xp = x.parent) == null ? null : xp.right;
                        

                        // s的左孩子为红色,去掉额外黑色
                        if (xpr != null) 
                            xpr.red = xp.red;
                            if ((sr = xpr.right) != null) 
                                sr.red = false;
                            
                        
                        if (xp != null) 
                            xp.red = false;
                            root = rotateLeft(root, xp);
                        
                        // 退出循环
                        x = root;
                    
                
             else 
                if (xpl != null && xpl.red) 
                    xpl.red = false;
                    xp.red = true;
                    root = rotateRight(root, xp);
                    xpl = (xp = x.parent) == null ? null : xp.left;
                
                if (xpl == null) 
                    x = xp;
                 else 
                    RBTreeNode<E> sl = xpl.left, sr = xpl.right;
                    if ((sl == null || !sl.red) &&
                        (sr == null || !sr.red)) 
                        xpl.red = true;
                        x = xp;
                     else 
                        if (sl == null || !sl.red) 
                            sr.red = false;
                            xpl.red = true;
                            root = rotateLeft(root, xpl);
                            xpl = (xp = x.parent) == null ?
                                null : xp.left;
                        
                        if (xpl != null) 
                            xpl.red = xp.red;
                            if ((sl = xpl.left) != null) 
                                sl.red = false;
                            
                        
                        if (xp != null) 
                            xp.red = false;
                            root = rotateRight(root, xp);
                        
                        x = root;
                    
                
            
        
    

查看源码

以上是关于Java实现数据结构——红黑树删除的主要内容,如果未能解决你的问题,请参考以下文章

红黑树插入与删除完整代码(dart语言实现)

数据结构 - 红黑树(Red Black Tree)删除详解与实现(Java)

Java实现红黑树

红黑树操作

红黑树数据结构剖析

红黑树数据结构剖析