普通人都能看懂的图解---红黑树的删除

本文详细介绍了红黑树的删除操作,包括没有左右子树、只有一个子树和拥有两个子树的情况,并探讨了颜色维度带来的6种可能情况。通过图形辅助理解,虽然复杂但有助于掌握红黑树的删除及平衡调整。附带了HashMap中红黑树删除的代码实现,但删除平衡部分理解困难,期待大佬指正。
摘要由CSDN通过智能技术生成
红黑树的删除

在上文中 . 我们已经介绍和演示了红黑树的插入 , 本文来 介绍一下 红黑树的 删除 以及 删除之后的调整

整理完之后,感觉还是疑点重重…本文代码 是解析 hashmap 的 红黑树操作…多处有疑问…希望有大佬能帮我梳理一下具体的操作
基于 我们对 AVL树的理解 , 用相同的方式 进行红黑树的删除分类

  • 被删除的 节点 没有左右子树
  • 被删除的 节点 只有左右子树的其中一个
  • 被删除的 节点 同时拥有左右子树

在这3大类的情况下 , 红黑树 同时又比 不同的树 多了一个 颜色维度 , 所以 再次细分 红 和 黑 表面上 有6种情况

不过不用害怕…大多数的情况 都不会发生, 好多种情况 之间 都能够重叠


话不多说看图学习 ( 后有总结 和 代码)

在这里插入图片描述


变化总结

每种情况遍历下来之后 - - 发现 还是比较复杂的…这里再整理一下

在这里插入图片描述


代码实现

突然发现实现起来 好像真的挺麻烦的- -…这里就截取 hashmap中的 红黑树操作… 大致整理了一下 它的思路 (删除平衡调节 那一块 看不太明白了)

/**
 * Removes the given node, that must be present before this call.
 * This is messier than typical red-black deletion code because we
 * cannot swap the contents of an interior node with a leaf
 * successor that is pinned by "next" pointers that are accessible
 * independently during traversal. So instead we swap the tree
 * linkages. If the current tree appears to have too few nodes,
 * the bin is converted back to a plain bin. (The test triggers
 * somewhere between 2 and 6 nodes, depending on tree structure).
 */
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                          boolean movable) {
    //--------------------------------------------------------------------
    int n;
    if (tab == null || (n = tab.length) == 0)
        return;
    int index = (n - 1) & hash;
    TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
    TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
    if (pred == null)
        tab[index] = first = succ;
    else
        pred.next = succ;
    if (succ != null)
        succ.prev = pred;
    if (first == null)
        return;
    if (root.parent != null)
        root = root.root();
    if (root == null
        || (movable
            && (root.right == null
                || (rl = root.left) == null
                || rl.left == null))) {
        tab[index] = first.untreeify(map);  // too small
        return;
    }
    //--------------这些东西是在判断长度是不是小于6了 , 如果小于6 就会转为链表------------------
    
    //下面才是正文
    TreeNode<K,V> p = this, pl = left, pr = right, replacement;
    if (pl != null && pr != null) {		//首先去判断删除的点是不是有这2孩子
        TreeNode<K,V> s = pr, sl;
        while ((sl = s.left) != null) 	//寻找该点的后继节点作为替代
            s = sl;
        boolean c = s.red; s.red = p.red; p.red = c; //交换 删除点 和 代替点的 颜色
        
        //------↓-↓----------↓----↓---------↓------↓-----
        TreeNode<K,V> sr = s.right;
        TreeNode<K,V> pp = p.parent;
        if (s == pr) { // p was s's direct parent
            p.parent = s;
            s.right = p;
        }
        else {
            TreeNode<K,V> sp = s.parent;
            if ((p.parent = sp) != null) {
                if (s == sp.left)
                    sp.left = p;
                else
                    sp.right = p;
            }
            if ((s.right = pr) != null)
                pr.parent = s;
        }
        p.left = null;
        if ((p.right = sr) != null)
            sr.parent = p;
        if ((s.left = pl) != null)
            pl.parent = s;
        if ((s.parent = pp) == null)
            root = s;
        else if (p == pp.left)
            pp.left = s;
        else
            pp.right = s;
        //--------这一段代码是在交换 代替点 和删除点的 位置 ↑---------
        
       
        //这边判断一下 被代替的位置 还有没有右子树...如果有的话 还需要进一步替代
        //也就是 replacement != p  而且 这个替代的 一定是红色
        if (sr != null)
            replacement = sr;
        else
            replacement = p;
    }
    
    //-------↓↓↓↓↓↓↓↓↓↓--仅有一支的情况----------
    else if (pl != null)		
        replacement = pl;
    else if (pr != null)
        replacement = pr;
    //-------↑↑↑↑↑↑↑↑↑--仅有一支的情况-----------
    
    else		//没有分支的情况
        replacement = p;
    
    // -------------↓↓-----  ↓↓↓↓↓ -----↓↓------------
    if (replacement != p) {			//如果还有代替的点  p 是删除的点 , 如果存在替代
        TreeNode<K,V> 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;
    }
    // ---------↑-----  将p点和代替点进行交换 , 然后p点脱离树-----↑↑------------
    
    //如果被删除的点是红色的...就不要调整平衡
    TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
    
    if (replacement == p) {  // detach
        TreeNode<K,V> pp = p.parent;
        p.parent = null;
        if (pp != null) {
            if (p == pp.left)
                pp.left = null;
            else if (p == pp.right)
                pp.right = null;
        }
    }
    if (movable)
        moveRootToFront(tab, r);
}

用于调整平衡的函数

//用来做删除的平衡 ( 被调用的情况 只有是在 p点为 红色的时候)
//一种情况 x 原本是p的孩子 (一定为 红色) 现在处于p的位置 , (p 已经被移除了)
//还有一种情况 x 就是 p (颜色 不确定)
//但是 这两种情况中 x 一定是叶子了 , 没有左右支
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                           TreeNode<K,V> x) {
    for (TreeNode<K,V> 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是红色
            x.red = false;
            return root;
        }
        else if ((xpl = xp.left) == x) {	//x 是黑色的...而且还在 左支上
            /**
            判断 x的兄弟节点 是红色的
                 黑(xp)                           黑(xpr)                     黑
                / \         变色, xp左旋          /   \                      /   \
               x   红(xpr)  ---------- >       红(xp)  黑  ---> 更新命名    红(xp)  黑
                  / \                          /  \                      /  \
                 黑  黑                       x(黑) 黑                  x(黑) 黑(xpr)
                 
            **/
            if ((xpr = xp.right) != null && xpr.red) {	
                xpr.red = false;
                xp.red = true;
                root = rotateLeft(root, xp);
                xpr = (xp = x.parent) == null ? null : xp.right;
            }
            if (xpr == null)	//x为黑的情况下 兄弟 为 null - -? 实在想不出这种情况
                x = xp;			//从这里开始 越来越奇怪!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            					//感觉 可能出现了什么偏差情况
            else {
                /**
                	?(xp)
                   /   \
                 x(黑) xpr(黑)
                       / \
                   sl(?)  sr(?)
                **/
                TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                /**
                	?(xp)       变色       ?(xp)
                   /   \     -------->   /   \
                 x(黑) xpr(黑)          x(黑)  xpr(红)
                **/
                if ((sr == null || !sr.red) &&
                    (sl == null || !sl.red)) {
                    xpr.red = true;
                    x = xp;
                }
                else {
                 	/**
                	?(xp)     变色,对xpr右旋    ?(xp)
                   /   \     ------------>   /   \
                 x(黑) xpr(黑)             x(黑)  xpr(sl)(黑)   <----命名调整
                      /             			   \
                     sl(红)						  (xpr)(红)
                **/
                    if (sr == null || !sr.red) {
                        if (sl != null)
                            sl.red = false;
                        xpr.red = true;
                        root = rotateRight(root, xpr);
                        xpr = (xp = x.parent) == null ?
                            null : xp.right;
                    }
                    if (xpr != null) {
                        xpr.red = (xp == null) ? false : 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 {
                TreeNode<K,V> 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) {
                        if (sr != null)
                            sr.red = false;
                        xpl.red = true;
                        root = rotateLeft(root, xpl);
                        xpl = (xp = x.parent) == null ?
                            null : xp.left;
                    }
                    if (xpl != null) {
                        xpl.red = (xp == null) ? false : xp.red;
                        if ((sl = xpl.left) != null)
                            sl.red = false;
                    }
                    if (xp != null) {
                        xp.red = false;
                        root = rotateRight(root, xp);
                    }
                    x = root;
                }
            }
        }
    }
}

左旋 和 右旋 操作函数

static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                      TreeNode<K,V> p) {
    TreeNode<K,V> r, pp, rl;
    if (p != null && (r = p.right) != null) {
        if ((rl = p.right = r.left) != null)
            rl.parent = p;
        if ((pp = r.parent = p.parent) == null)
            (root = r).red = false;
        else if (pp.left == p)
            pp.left = r;
        else
            pp.right = r;
        r.left = p;
        p.parent = r;
    }
    return root;
}

static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                       TreeNode<K,V> p) {
    TreeNode<K,V> l, pp, lr;
    if (p != null && (l = p.left) != null) {
        if ((lr = p.left = l.right) != null)
            lr.parent = p;
        if ((pp = l.parent = p.parent) == null)
            (root = l).red = false;
        else if (pp.right == p)
            pp.right = l;
        else
            pp.left = l;
        l.right = p;
        p.parent = l;
    }
    return root;
}

感谢观看

希望有大佬指正错误! 谢谢(士下座)!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值