zoukankan      html  css  js  c++  java
  • 数据结构~基础2~树【《二叉树、二叉搜索树、AVL树、B树、红黑树》的设计】~红黑树

     数据结构~基础2~树【《二叉树、二叉搜索树、AVL树、B树、红黑树》的设计】~红黑树

    一、 红黑树

    红黑树之介绍:

    -----------形态上是特殊的二叉搜索树【特殊体现在颜色上,同时在逻辑上它是等价于4阶B树的】

    ❀ 红黑树是怎么等价于4 阶B 树的? ---------红黑树要变成B树:需要将红结点和黑结点进行合并(黑色作为根【也是中间元素】)

     红黑-->B 树: 结点有四种情况是:①红-黑-红、②红-黑、③黑-红、④黑

    ❀ 红黑树的通用接口:二叉搜索树的通用接口 + 增加之后、删掉之后

    旋转【左旋、右旋】、旋转之后的处理【更新父结点的关系】(旋转、旋转之后跟AVL 树一样)

    ■ 插入的所有位置情况:

    ■ 增加之后:

    ❀ 总结红黑树的添加之后的调整 ❀ :

    1,分类:(具体过程需要根据父结点作为左结点、右结点进行对称)

    (1)不需要调整:

      ■ 当前结点是根,染黑即可;

      ■ 父结点是黑,不用处理。

    (2)需要调整:(根据叔父结点是否为红色进行划分)

      ■ case1:叔父是红色,当前结点x 可左可右,染黑 父、叔,染红爷,回溯到结点爷处。

      ■ case2:叔父是黑色,当前结点x 是右孩子【红红是LR型】,先进行左旋,转化成case3;

    (先考虑当前结点是右孩子,因为它处理一下就变成了case3)【小细节,当前结点x 指向 父结点时,旋转x(其实旋转的是原先的父结点的位置)】;

      ■ case3:叔父是黑色,当前结点x 是左孩子【红红是LL型】,染黑父,染红爷,然后右旋。

        @Override
        protected void afterAdd(Node<E> node) {
            // 判断父结点
            Node<E> parent = node.parent;
            // 添加的是根【当添加第一个结点时】/ 或者上溢到了根结点时
            if (parent == null) {
                black(node);
                return;
            }
            // 若父结点是黑色时,不用处理,直接返回
            if (isBlack(parent))
                return;
    
            // 若叔父结点是红色的[B 树的上溢]
            Node<E> uncle = parent.sibling();
            Node<E> grand = red(parent.parent);
            if (isRed(uncle)) {
                // 染黑爸爸、叔叔,把祖父染成红色,相当于新插入的结点
                black(uncle);
                black(parent);
                // ① 上溢时,也要染红祖父结点
                afterAdd(grand);
                return;
            }
            // 观察一下,对代码进行优化,共同拥有的抽到外部之类的
            // 来到这里叔父不是红色
            if (parent.isLeftChild()) { // L
                // ② 旋转时,也要 染红结点
                if (node.isLeftChild()) { // LL
                    // 染黑父结点,染红祖父结点
                    black(parent);
                    // 右旋
                } else { // LR
                    // 染红自己、染黑祖父结点
                    black(node);
                    // 左旋后右旋
                    rotateLeft(parent);
                }
    
                rotateRight(grand);
            } else { // R
                // ② 旋转时,也要 染红结点
                if (node.isLeftChild()) { // RL
                    // 染红自己、染黑祖父结点
                    black(node);
                    // 左旋后右旋
                    rotateRight(parent);
                } else { // RR
                    // 染黑父结点,染红祖父结点
                    black(parent);
                    // 左旋
                }
    
                rotateLeft(grand);
            }
        }

    ■ 删除的所有位置情况:

    ■ 删除之后:

    ❀ 总结红黑树的删除之后的调整 ❀ :

    1,删除结点是红色,(完美),不用处理;

    2,删除结点是黑色:【看替代结点--前驱/后驱结点】

    (1)替代结点是红色,染黑替代结点【前驱/后驱结点】

    (2)替代结点是黑色,(若是根,完美,不用处理),黑色结点

    ● 黑色叶子结点:【看兄弟】:----- 站在B树角度,发生了下溢

    【B树中的处理,找兄弟借,兄弟借不了,找父结点下来合并】

    [1] 兄弟是黑色,且能借(至少有一个子红色结点),旋转借出去。

    【直接借的话,不符合二叉搜索树的特点,根大于左子树,根小于右子树(需要旋转交换一下结点,(符合二叉搜索树特点)然后再借)】。

    [2] 兄弟是黑色,不能借(没有红色结点),看父结点:

    ① 父结点是红色:染黑父结点,染红兄弟,进行合并。

    ② 父结点是黑色,下溢处理。

    [3] 兄弟是红色,通过旋转,把侄子(黑色结点)变成兄弟【又变成了删除的结点的兄弟结点是黑色结点】

     

     

    ● 黑色叶子结点:【看兄弟】:

    [3] 兄弟是红色,通过旋转,把侄子(黑色结点)变成兄弟【又变成了删除的结点的兄弟结点是黑色结点】:

    只能是图示:(兄弟跟父结点在同一个B树的子结点上(同一层上),且红色兄弟有两个黑色的子结点)

    删除黑色叶子结点时,发生了下溢,而显然不在同一层上的兄弟肯定是不能借出结点,

    只能考虑跟自己处于同一层【兄弟的儿子】,向兄弟的儿子进行借。【但是在B树中借的前提,两者是兄弟关系】

    所以目标就是要将兄弟的儿子,变成我的兄弟才能顺利成章的借。

    [2] 兄弟是黑色,不能借(没有红色结点),看父结点:

    ① 父结点是红色:染黑父结点,染红兄弟,进行合并。

          

    [2] 兄弟是黑色,不能借(没有红色结点),看父结点:

    ② 父结点是黑色,下溢处理。

     

    [1] 兄弟是黑色,且能借(至少有一个子红色结点),旋转借出去。【旋转的结果是变成了根(成为独立的B树结点),就需要将其染黑】

    【直接借的话,不符合二叉搜索树的特点,根大于左子树,根小于右子树(需要旋转交换一下结点,(符合二叉搜索树特点)然后再借)】。

        protected void afterRemove2(Node<E> node, Node<E> replacement) {
            //删除结点是红色 或者 替代结点【前驱/后驱】是红色
            if(isRed(node))    return;
            if (isRed(replacement)) {
                black(replacement);
                return;
            }
            //接下来考虑删除结点是黑色【排除掉根的情况】
            Node<E> parent = node.parent;
            // 删除的是根节点
            if (parent == null)
                return;
    
            // 删除黑色叶子节点【下溢】
            boolean left = parent.left == null || node.isLeftChild();// 判断被删除的node是左还是右
            Node<E> sibling = left ? parent.right : parent.left;
            if (left) { // 被删除的节点在左边,兄弟节点在右边
                
                //看兄弟【兄弟是红色,染黑兄弟,通过左旋,让兄弟成为根,更新一下兄弟位置】
                if (isRed(sibling)) { // 兄弟节点是红色
                    black(sibling);
                    red(parent);
                    rotateLeft(parent);
                    // 重新指一下兄弟(更新一下兄弟位置)
                    sibling = parent.right;
                }
    
                // 【兄弟结点是黑色,兄弟没有红色子结点,不能借】~下溢情况
                if (isBlack(sibling.left) && isBlack(sibling.right)) {
                    // 兄弟节点借不出,父节点要向下跟兄弟节点合并【父结点作为下来结点的根】
                    boolean parentBlack = isBlack(parent);
                    black(parent);
                    red(sibling);
                    if (parentBlack) {//若是父结点原先是黑色,导致塌陷,递归
                        afterRemove2(parent, null);
                    }
                // 【兄弟结点是黑色,兄弟有红色子结点可以借】    
                } else { 
                    // 兄弟节点的左结点是黑色,兄弟要先旋转
                    if (isBlack(sibling.right)) {
                        rotateRight(sibling);
                        //更新一下兄弟结点
                        sibling = parent.right;
                    }
                    //旋转之后,旋转之后的中心节点继承 parent 的颜色;
                    //旋转之后的左右节点染为 BLACK ----【成为独立的B树结点】
                    color(sibling, colorOf(parent));
                    black(sibling.right);
                    black(parent);
                    rotateLeft(parent);
                }
    
            } else { // 被删除的节点在右边,兄弟节点在左边
                if (isRed(sibling)) { // 兄弟节点是红色
                    black(sibling);
                    red(parent);
                    rotateRight(parent);
                    // 更换兄弟
                    sibling = parent.left;
                }
    
                // 兄弟节点必然是黑色
                if (isBlack(sibling.left) && isBlack(sibling.right)) {
                    // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
                    boolean parentBlack = isBlack(parent);
                    black(parent);
                    red(sibling);
                    if (parentBlack) {
                        afterRemove2(parent,null);
                    }
                } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
                    // 兄弟节点的左边是黑色,兄弟要先旋转
                    if (isBlack(sibling.left)) {
                        rotateLeft(sibling);
                        sibling = parent.left;
                    }
    
                    color(sibling, colorOf(parent));
                    black(sibling.left);
                    black(parent);
                    rotateRight(parent);
                }
            }
        }
  • 相关阅读:
    DC中为什么要用Uniquify?
    hdu 1596 find the safest road
    hdu2112 HDU Today
    hdu 2066 一个人的旅行
    poj 3026 Borg Maze
    poj 1979 Red and Black
    poj 1321 棋盘问题
    hdu 1010 Tempter of the Bone
    hdu 4861 Couple doubi
    codeforces584B Kolya and Tanya
  • 原文地址:https://www.cnblogs.com/shan333/p/15496806.html
Copyright © 2011-2022 走看看