红黑树的增加(插入)和删除

简介: 红黑树的增加(插入)和删除

红黑树的增加(插入)和删除


 

☼ 红黑树之介绍:

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

 

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

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

 

● 可以插入的位置的所有情况:


43.png



◼ 红黑树必须满足以下 5 条性质:

1. 节点是 RED 或者 BLACK

2. 根节点是 BLACK

3. 叶子节点(外部节点,空节点)都是 BLACK

4. RED 节点的子节点都是 BLACK:

✓ RED 节点的 parent 都是 BLACK

✓ 从根节点到叶子节点的所有路径上不能有 2 个连续的 RED 节点

5. 从任一节点到叶子节点的所有路径都包含相同数目的 BLACK 节点

 

一、红黑树结点的添加(插入)--添加必是添加到 -B树叶子子结点的位置:

(1) 4阶 B 树的叶子结点一般有以下四种情况:

①    红<---黑--->红    ② 红<---        ③ --->红      ④

(2) 分类讨论:

(2-1)当父结点是黑色(有四种情况)时【上图的 7、8、11、12】,直接插入;【有可能是第一个结点-根(记得染黑根)】;

(2-2)剩下8种情况根据 叔父结点是否为红色进行划分:

(2-2-1)叔父是红色时【上图的 1、2、3、4】,举个栗子:  (新插入的)<---<---【根】--->红 (对于4阶B树而言,发生了上溢了,需要,进行分裂处理根(染红)上去,然后父结点、叔父结点染黑);即 :


44.png


(2-2-2)叔父不是红色时【上图的 5、6、9、10】,举个栗子:   (新插入的)<---<---【根】:为了满足红黑树定义的性质四:

  (新插入的)<---红(变黑)<---【根】(变红因为B树的结点组合中是红黑红(只有一个黑作为中间元素的根)

 ●  现在需要修改:中间的黑色为根,需要修改那个<---的方向:改为:

 (新插入的)<---红(变黑) 【根】--->【根】(变红)

[观察此刻情况,修改这种修改,符合右旋,再观察其实就是跟AVL 的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);
//            red(grand);
//            afterAdd(grand);
            // ① 上溢时,也要染红祖父结点
//            afterAdd(red(grand));
            afterAdd(grand);
            return;
        }
        //观察一下,对代码进行优化,共同拥有的抽到外部之类的
        // 来到这里叔父不是红色
        if (parent.isLeftChild()) { // L
            // ② 旋转时,也要 染红结点
//            red(grand);
            if (node.isLeftChild()) { // LL
                //染黑父结点,染红祖父结点
                black(parent);
//                red(grand);
                //右旋
//                rotateRight(grand);
            } else { // LR
                //染红自己、染黑祖父结点
                black(node);
//                red(grand);
                //左旋后右旋
                rotateLeft(parent);
//                rotateRight(grand);
            }
            rotateRight(grand);
        } else { // R
            // ② 旋转时,也要 染红结点
//            red(grand);
            if (node.isLeftChild()) { // RL
                //染红自己、染黑祖父结点
                black(node);
//                red(grand);
                //左旋后右旋
                rotateRight(parent);
//                rotateLeft(grand);
            } else { // RR
                //染黑父结点,染红祖父结点
                black(parent);
//                red(grand);
                //左旋
//                rotateLeft(grand);
            }
            rotateLeft(grand);
        }
    }



❀ 总结 红黑树的添加❀ :

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

(1)不需要调整:

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

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

(2)需要调整:

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

  ■ case2:叔父是黑色,当前结点x 是右孩子【红红是LR型】,先进行左旋,转化成case3;(先考虑当前结点是右孩子,因为它处理一下就变成了case3)【小细节,当前结点x 指向 父结点时,旋转x(其实旋转的是原先的父结点的位置)】

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

 

 

二、红黑树结点的删除--删除必是 -B树叶子子结点的位置:

● 可以删除的位置的所有情况:


48.png


(1) 4阶 B 树的叶子结点一般有以下四种情况:

①    红<---黑--->红    ② 红<---        ③ --->红      ④

(2) 分类讨论:


49.png


(2-1) 删除的直接是红色结点时【上图的 1、3、5、6】【2也一样(因为它是度为2,最终删除的要么是前驱的1或者后驱的3)就直接删除,不用处理。

(2-2) 删除的是黑色的结点:

(2-2-1)用来替代的结点是红色时【上图的 4、7】处理:将替代的结点【5、6】染成黑色(因为替代成为了B树的叶子结点了,根是黑色的)。

(2-2-2)用来替代的结点是黑色时【上图的 8】,处理:


50.png


 ❀ 红黑树整个删除结点之后的处理的代码如下:


@Override
    protected void afterRemove(Node<E> node) {
        //要删除结点是红色,直接删除,不用处理
//        if(isRed(node))    return;
        //要删除的结点是黑色 ,然后用于替换删除结点的子结点是红色,然后替换结点即可
        if(isRed(node)) {    //这里的处理是包含了上面if(isRed(node))的情况
            black(node);
            return;
        }
        Node<E> parent = node.parent;
        //删除的是根结点,也是直接删除不用处理
        if(parent == null)    return;
        // 要删除结点是黑色,而且是叶子结点,没有可以替换的结点时,
        // 判断被删除的node 是左还是右,直接通过sibling 去拿就已经不准了,因为叶子结点已经删除了,拿不到哦 了
        // 需重新判断一下
        boolean left = parent.left == null || node.isLeftChild();
        Node<E> sibling = left ? parent.right : parent.left;
        if (left) { // 被删除的是左边的,兄弟结点在右边
            if (isRed(sibling)) { // 兄弟结点是红色,相当于以前删除时,先考虑删除度为2,将度为2的进行第一步处理后,它就变成了与删除度为1、0 的情况一致了
                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) {
                    // 下溢,递归处理,相当于父结点被删除
                    afterRemove(parent);
                }
            } else { // 兄弟结点至少有一个红色子结点【可以外借时】
                // 即接下来的情况: 黑【根】->红 红<-黑【根】 红<-黑【根】->红
                // 兄弟结点的右边是黑色,兄弟要先旋转【即先处理情况: 黑->红,处理完后,接下来变成了 RR的情况,与后边两者是一样只需要左旋转】
                if (isBlack(sibling.right)) {
                    rotateRight(sibling);
                    sibling = parent.right; // 修改兄弟结点位置
                }
                // 染色处理【上去的兄弟,要保证颜色与原来的相同,结构不变 】
                color(sibling, colorOf(parent));
                // 兄弟的儿子【可以借的结点】要上去作为根
                black(sibling.right);
                // 父结点要旋转下来作为根
                black(parent);
                rotateLeft(parent);
            }
        } else { // 删除结点是在右边的,兄弟结点在左边的
            if (isRed(sibling)) { // 兄弟结点是红色,相当于以前删除时,先考虑删除度为2,将度为2的进行第一步处理后,它就变成了与删除度为1、0 的情况一致了
                black(sibling); // 等下变成根
                red(parent);
                rotateRight(parent);// 通过右边旋转,侄子变成了我的兄弟了
                // 更换兄弟
                sibling = parent.left;
            }
            // 现在来到这里的兄弟都是黑色的啦
            if (isBlack(sibling.left) && isBlack(sibling.right)) { // 黑兄弟的兄弟是黑色的,不能借【没有红色结点可以外借】
                // 借不了,只能让父结点下来进行跟兄弟合并【将父结点染黑(变成根),兄弟染红】
                // 染色之前还需要考虑父结点原先是不是黑色【黑色会导致下溢】
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    // 下溢,递归处理,相当于父结点被删除
                    afterRemove(parent);
                }
            } else { // 兄弟结点至少有一个红色子结点【可以外借时】
                // 即接下来的情况: 黑【根】->红 红<-黑【根】 红<-黑【根】->红
                // 兄弟结点的左边是黑色,兄弟要先旋转【即先处理情况: 黑->红,处理完后,接下来变成了 LL的情况,与后边两者是一样只需要右旋转】
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling);
                    sibling = parent.left; // 修改兄弟结点位置
                }
                // 染色处理【上去的兄弟,要保证颜色与原来的相同,结构不变 】
                color(sibling, colorOf(parent));
                // 兄弟的儿子【可以借的结点】要上去作为根
                black(sibling.left);
                // 父结点要旋转下来作为根
                black(parent);
                rotateRight(parent);
            }
        }
    }



目录
相关文章
|
11月前
|
机器学习/深度学习 数据采集 存储
通义千问 Qwen 在智能文本分析中的应用实践
本文探讨了通义千问Qwen在智能文本分析的应用,涵盖文本分类、情感分析及关键信息提取,通过具体案例和代码实现,展示了Qwen的强大语言理解能力,为开发者和研究人员提供了实用参考。
|
分布式计算 并行计算 数据处理
大规模数据处理的最佳实践:使用 Dask 进行高效并行计算
【8月更文第29天】在大数据时代,高效地处理大规模数据集是至关重要的。Python 社区提供了一些强大的工具来帮助开发者进行并行和分布式计算,其中之一就是 Dask。本文将详细介绍如何使用 Dask 来优化大规模数据集的处理效率,并提供一些实用的代码示例。
2006 3
|
SQL PHP 数据库
19 PHP如何利用PDO获取结果集
路老师在知乎上分享了关于PHP语言的知识,帮助大家入门并深入了解PHP。本文介绍了PDO中获取结果集的三种方法:`fetch()`、`fetchAll()` 和 `fetchColumn()`,并通过具体案例展示了如何使用这些方法从数据库中获取数据并展示在网页上。
439 5
|
SQL 关系型数据库 MySQL
【PHP开发专栏】PHP与数据库交互入门
【4月更文挑战第29天】本文介绍了PHP与数据库交互的基础,包括选择MySQL或PostgreSQL等关系型数据库,使用MySQLi或PDO扩展进行连接。示例展示了如何使用PHP连接数据库,如MySQLi的面向对象连接方式和PDO的多数据库支持。此外,还讲解了执行SQL查询(如SELECT、INSERT、UPDATE、DELETE)的操作,并强调了安全性与错误处理,如使用预处理语句防止SQL注入。通过学习,读者可掌握PHP操作数据库的基本技能。
188 0
|
缓存 自然语言处理 大数据
ModelScope问题之运行模型报错如何解决
ModelScope模型报错是指在使用ModelScope平台进行模型训练或部署时遇到的错误和问题;本合集将收集ModelScope模型报错的常见情况和排查方法,帮助用户快速定位问题并采取有效措施。
2287 0
|
Java 数据处理
Java:将一个数转换为十六进制
Java:将一个数转换为十六进制
|
SQL 关系型数据库 MySQL
MySQL多实例部署:从概念到实操的全面指南
MySQL多实例部署:从概念到实操的全面指南
497 0
|
算法 Java
红黑树(下)完整删除过程
红黑树一般用在较为底层的地方作为保证效率的数据结构, 且红黑树的删除算法特别复杂!了解即可,手写出的难度较大。 对于删除算法,很多书上没有提及,或者写的很混乱。 全网亦没有一篇文章通俗易懂的讲清楚了其中过程, 此文也是参考了几篇大牛的博文,部分为笔者原创,部分为引用整合。 红黑树的删除其实就是基于二叉搜索树的删除上加入一个调色过程。 在弄清楚红黑树的删除操作之前,需要明白二叉搜索树的删除方法。 首先要明白几个概念:
377 0
|
C语言
万字超全详解:二叉树的基本操作(C语言版本)(上)
万字超全详解:二叉树的基本操作(C语言版本)(上)
万字超全详解:二叉树的基本操作(C语言版本)(上)
|
JavaScript 前端开发 程序员
修改vue-element-admin左侧导航栏的图标
修改vue-element-admin左侧导航栏的图标
363 0