[日记]LeetCode算法·二十六——二叉树⑥ 红黑树(插入与删除,附图)

news2024/11/22 16:20:59

承接上一篇AVL树AVL树,红黑树相较于AVL树,就相当于完全二叉树相当于AVL树,如何在性能退化和维护成本之间做出CS中经典的trade-off

文章目录

  • 红黑树的概念
  • 红黑树查询效率
  • 红黑树的插入
    • 1 插入节点N为根节点
    • 2 插入节点N的父节点P为黑色
    • 3 N的父节点P为红色,且叔叔节点U也为红色
    • 4 父节点P为红色,叔叔节点U为黑色,P为左孩子,N为右孩子
    • 5 父节点P为红色,叔叔节点U为黑色,P为左孩子,N为左孩子
    • 红黑树插入总结
  • 红黑树的删除
    • 1 删除节点X为红色
    • 2 替换节点N为根节点
    • 3 S为黑,S右孩子为红,N为P的左孩子
    • 4 S为黑色,S左孩子为红,S右孩子为黑,N为P的做孩子
    • 5 S为红色,其余节点为黑色
    • 6 N、P、S、SL、SR全为黑
    • 7 P为红,N、S、SL、SR为黑
    • 删除总结
  • 总结

红黑树的概念

相较于AVL树,通过平衡因子维护一个绝对平衡的二叉树,红黑树采用不严格平衡,从而减少了对树结构的调整,在大量IO的情况下有着更加优秀的性能。
红黑树是根据以下的性质定义,实现自平衡

  1. 节点是红色或者黑色
  2. 根节点是黑色
  3. 叶节点是黑色(这里的叶节点表示nullptr节点,而非一般的叶节点)
  4. 每个红色节点的子节点都是黑色的(不能有连续的两个红色节点)
  5. 从任意节点到其每个叶子的简单路径都包含相同数量的黑色节点(黑高一致)

这里需要注意的是,红黑树的叶节点并非左右子树都为nullptr的节点,而是nullptr节点。
其中相对关键的性质是性质4性质5,通过这两条性质,我们就可以保证红黑树的搜索效率为O(logn)

红黑树查询效率

为何通过以上的5条性质就可以保证红黑树的性能不会退化严重,依然可以维持在O(logn),类似于AVL树,我们可以采用数学归纳法进行证明。
首先我们证明一个引理:
以任意节点 x 为根的子树中包含至少 2 b h ( x ) − 1 个内部节点 以任意节点x为根的子树中包含至少2^{bh(x)}-1个内部节点 以任意节点x为根的子树中包含至少2bh(x)1个内部节点 b h ( x ) 为 x 的黑高,即从节点 x 出发到达一个叶节点的任意一条简单路径上黑色节点的数量 ( 不包含 x 本身 ) bh(x)为x的黑高,即从节点x出发到达一个叶节点的任意一条简单路径上黑色节点的数量(不包含x本身) bh(x)x的黑高,即从节点x出发到达一个叶节点的任意一条简单路径上黑色节点的数量(不包含x本身)

  1. b h ( x ) = 0 bh(x)=0 bh(x)=0时, x x x为叶子结点,以 x x x为根节点的子树包含0个内部节点,结论成立。
  2. 考虑一个高度为正值且有两个子节点的内部节点 x x x。每个子节点的黑高为 b h ( x ) bh(x) bh(x) b h ( x ) − 1 bh(x)-1 bh(x)1,这取决于子节点本身是红色 b h ( x ) bh(x) bh(x)还是黑色 b h ( x ) − 1 bh(x)-1 bh(x)1。因此,以 x x x为根节点的子树,至少包含 2 b h ( x ) − 1 − 1 + 2 b h ( x ) − 1 − 1 + 1 = 2 b h ( x ) − 1 个节点 2^{bh(x)-1}-1+2^{bh(x)-1}-1+1=2^{bh(x)}-1个节点 2bh(x)11+2bh(x)11+1=2bh(x)1个节点,证明完毕。

另外根据性质4,我们可以知道: 从根节点到叶节点的任意一条简单路径上至少有 1 2 的节点为黑色节点 从根节点到叶节点的任意一条简单路径上至少有\frac{1}{2}的节点为黑色节点 从根节点到叶节点的任意一条简单路径上至少有21的节点为黑色节点即黑高至少为高度的一半,因此,假设一个最糟糕的红黑树,其节点数为n,高度为h,则黑高 b h ≥ 1 2 h bh\geq\frac{1}{2}h bh21h,根据引理,我们有 n ≥ 2 b h ( x ) − 1 ≥ 2 h 2 − 1 n\geq2^{bh(x)}-1\geq2^{\frac{h}{2}}-1 n2bh(x)122h1 故 h ≤ 2 l o g ( n + 1 ) 故h\le2log(n+1) h2log(n+1)因此红黑树的查询效率为O(logn)

红黑树的插入

红黑树查询与BST没有区别,在此关注红黑树的插入与删除,另外注明本文只关注节点N或父节点P位于左子树的情况,右子树的情况可以根据对称得到。
关于插入,我们可以明确的一点是,应该插入节点应为红色,因为插入红色节点不会影响性质5,即黑高不变
在此我们列出插入节点的若干种情况,以及对应的处理方法。

1 插入节点N为根节点

此时,我们只需要将插入节点进行染色,红→黑即可,如图所示
在这里插入图片描述

2 插入节点N的父节点P为黑色

此时我们不需要进行任何操作,因为插入红节点不影响性质5,且父节点P为黑色不影响性质4
在这里插入图片描述

3 N的父节点P为红色,且叔叔节点U也为红色

如图所示
在这里插入图片描述
此时违反了性质4,我们进行的处理如下:

  • 将P和U染为黑色
  • 将祖父节点G染为红色
  • 下一步关注组父节点G,进一步递归判断

之所以需要递归判断,是因为G的父节点可能为红色,再一次违反性质4

4 父节点P为红色,叔叔节点U为黑色,P为左孩子,N为右孩子

如图所示
在这里插入图片描述

注意,这里所对应的还有P为G的右孩子,N为P的左子树的对称情况,在此不再赘述。
我们对这种情况需要进行的处理为N、P左旋,下一步关注P节点。
此时左旋后,我们的红黑树依然违反了性质4,因此进一步关注节点P,并且通过情况5进行处理

5 父节点P为红色,叔叔节点U为黑色,P为左孩子,N为左孩子

如图所示
在这里插入图片描述

注意,这里所对应的还有P为G的右孩子,N为P的右孩子的对称情况,在此不再赘述。
此时我们的处理方法如下:

  1. PG右旋
  2. P染黑
  3. G染红

通过以上步骤,我们发现,各个点位的黑高都没有发生变化,且去除了相邻的红色节点,因此完成了红黑树的调整

红黑树插入总结

  1. 找到插入的位置,将红色新节点N插入
  2. 判断N是否是根节点,是的话,染为黑色,否则继续
  3. 判断N的父节点是否为黑色,是的话,返回,否则继续
  4. 判断N的叔叔节点是否为红色,是的话,将P、U染为黑,G染为红,N=G,进一步判断
  5. 判断N所在的分支是否和P所在的分支不一致,不一致的话,则进行旋转,上升N,下降P,并归为一边,继续判断
  6. 若P和N所在分支一致,则对P、G进行旋转,上升P,下降G,并交换P与G的颜色,返回

红黑树的删除

相比于插入,红黑树的删除也确实更加复杂。同样适用替换删除法,删除前驱或后继节点。
和AVL树一致,我们不关心最终删除的节点是否与想删除的一致,我们只关心被删除的节点,如果被删除的节点为红色,则较为简单,否则会比较复杂,我们将进一步的进行分析,同样地,以下分析依然基于N为左子树或P为左子树的情况,对称的情况不再赘述
以下各节点的名字所代表的含义分别如下:

  1. X为最终被删除的节点
  2. N为替换X的节点
  3. S为X的兄弟节点,替换后也就是N的兄弟节点
  4. SL为S的左节点,SR为S的右节点
  5. P为N的父节点
  6. G为P的父节点,N的组父节点

1 删除节点X为红色

此时直接删除节点X,并用黑色的N节点(必然是黑色)替换,则不会影响性质4与性质5,是最为简单的一种删除情况。

2 替换节点N为根节点

替换后节点N为根节点,此时我们将节点N染色为黑色,相当于所有节点都去出了一个黑色节点,黑高一致,完毕。

3 S为黑,S右孩子为红,N为P的左孩子

如图所示,其中蓝色的P节点与SL节点,代表这些节点可红可黑。
在这里插入图片描述

之所以上来就考察如此复杂的情况,是因为这种情况可以通过一定操作完成删除的调整。同理,可以适用于N为右孩子,S左孩子为红的对称情况
具体操作如下:

  1. P、S左旋
  2. P与S交换颜色
  3. SR染色为黑

让我们对删除前后的子树进行分析。

  • 我们假设, C ( x ) C(x) C(x)为判断节点x是否为黑色的函数,若为黑色,则 C ( x ) = 1 C(x)=1 C(x)=1,反之, C ( x ) = 0 C(x)=0 C(x)=0
  • 删除前, b h ( G → N ) = G → P → X → N = 2 + C ( P ) bh(G→N)=G→P→X→N=2+C(P) bh(GN)=GPXN=2+C(P) b h ( G → S L ) = G → P → S → S L = 1 + C ( P ) + C ( S L ) bh(G→SL)=G→P→S→SL=1+C(P)+C(SL) bh(GSL)=GPSSL=1+C(P)+C(SL) b h ( G → S R ) = G → P → S → S R = 1 + C ( P ) bh(G→SR)=G→P→S→SR=1+C(P) bh(GSR)=GPSSR=1+C(P)
  • 删除后, G → S L 与 G → S R G→SL与G→SR GSLGSR不变, G → N G→N GN改变为 1 + C ( P ) 1+C(P) 1+C(P)
  • 旋转后, b h ( G → N ) = G → S → P → N = 2 + C ( P ) bh(G→N)=G→S→P→N=2+C(P) bh(GN)=GSPN=2+C(P) b h ( G → S L ) = G → S → P → S L = 1 + C ( P ) + C ( S L ) bh(G→SL)=G→S→P→SL=1+C(P)+C(SL) bh(GSL)=GSPSL=1+C(P)+C(SL) b h ( G → S R ) = G → S → S R = 1 + C ( P ) bh(G→SR)=G→S→SR=1+C(P) bh(GSR)=GSSR=1+C(P),因为S和P的对调与颜色互换,使得经过SL的黑高不变的同时,N的黑高+1,恢复到了删除前。而通过对SR的染色,弥补了S染为 C ( P ) C(P) C(P)时的黑高减少,旋转前后根节点颜色不变,意味着不需要继续向上迭代
  • 旋转前后,针对之前的P和S来看,N侧的黑高+1

4 S为黑色,S左孩子为红,S右孩子为黑,N为P的做孩子

不再赘述对称情况,直接上图
在这里插入图片描述

操作如下:

  1. SL、S右旋
  2. SL染为黑色
  3. S染为红色
  4. 转入情况3处理

通过分析,旋转前后,不影响P到右子树任何叶节点的黑高。

5 S为红色,其余节点为黑色

如图所示
在这里插入图片描述

我们已经意识到在黑节点之中插入1个红节点,不会影响红黑树的定义,在这种情况下,我们面对S为红,其余为黑的操作如下:

  1. P、S左旋
  2. P染红
  3. S染黑
  4. 转入情况3情况4

6 N、P、S、SL、SR全为黑

如图所示
在这里插入图片描述

我们需要做的是将S染红将关注节点设为P
相比于删除前,N和S的黑高同时下降1,P的左右子树保持了一致,即P满足红黑树定义,但经过P的路径,将比不经过P的路径黑高小1这相当于P成为了替换节点,其父节点被删去的情况,因此根据P的父节点、兄弟节点进行进一步的判断,直到转入情况1或2或3。

7 P为红,N、S、SL、SR为黑

如图所示
在这里插入图片描述

此时我们交换P与S的颜色,S黑高减1,与N保持一致,相当于P的黑高减一与情况6染色后情况一致将关注节点设为P,进一步判断。

删除总结

分析了各种情况的删除,我们可以做一些简单的总结。

  • 情况1、2、3,可以通过一定的处理,完成对红黑树的调整
  • 情况4通过不改变黑高的方法,转换为了情况3
  • 情况5通过不改变黑高的方法,转化为了情况3或4
  • 情况6和7,通过改变另一侧的黑高,完成了P的子树调整,但过P和不过P产生了黑高的不一致,与P为替换节点的情况一致,需要对P进行进一步的判断
  • 以上操作都建立在P或N为左子树情况,右子树情况进行对称即可。

总结

峣峣者易折,皎皎者易污。这句话用来形容AVL树、红黑树、最佳排序树实在再合适不过。
最佳的性质最容易破坏而难以保持,且“最佳”往往是一种全局性质无法从局部进行把握和检查,一旦被破坏,也无法从局部调整恢复,这就是最佳排序树(这个的博客还没出)往往应用较少的原因。
AVL树和红黑树则都是在检索效率和动态操作之间做出了取舍,纵观两者,其搜索效率性能都发生了常数级的退化,但同时也支持了局部的动态调整操作,两者的插入与删除均维持在O(logn)。而红黑树则是对检索效率的进一步放弃以及更加复杂的算法复杂度,换取了更少的旋转操作,无论是C++STL中的map,还是Java中的TreeMap底层都是红黑树的实现。
——2023.5.18

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/540758.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

雷鸟Air Plus体验:视觉大幅升级,影视/办公/游戏全能胜任

雷鸟BirdBath系列XR眼镜一直保持着较快的迭代频率,如今迎来该系列第三款产品:雷鸟Air Plus,新品在视觉体验上得到大幅升级,不仅FOV达到49,边缘成像质量更高,搭配索尼旗舰级Micro OLED屏实现最高120Hz刷新率…

LoRa温振变送器的类型特点及技术参数

自电器文明开始,短短一个世纪,我们对科学探索已经从工业到达了智能化的程度。传感器的应用,不断为时代带来惊喜,各行各业都在争相抓紧契机,利用科技不断创新,推动着整个传感器行业的发展。在工业自动化系统…

redis单机版本Java代码实践

pom依赖 引入redis的starter以及commons-pool2的依赖&#xff0c;commons-pool2是配置连接池需要使用的&#xff0c;不引入&#xff0c;只有连接池配置是不会创建连接池的 <!-- redis --><dependency><groupId>org.springframework.boot</groupId><…

k8s的一些基本概念

目录 masterNodePod和ContainerControllerService和IngressLabelSecretVolumeReplicationController和ReplicaSetDeploymentDaemonSetJobCronJobStatefulSetNamespace 推荐k8s中文文档&#xff0c;里面有各种概念的具体用法http://docs.kubernetes.org.cn/437.html 一个k8s集群…

Spring Boot 整合MyBatis 和 Spring Boot 整合MyBatis-Plus

目录 Spring Boot 整合MyBatis 代码配置实现 创建数据库和表 使用灵活的方式创建maven 创建resources/application.yml &#xff0c;配置数据源参数, 并完成Spring Boot 项目启动测试 测试DruidDataSourceConfig 创建MonsterMapper接口 创建resources\mapper\MonsterMap…

开发笔记之:文件读取溢出分析(QT C++版)

&#xff08;1&#xff09;引言 以下是QT C读取数据文件&#xff08;QDataStream&#xff09;的代码&#xff1a; /*** 按双字读取* param fis 文件输入流* param isBigEndian 是否大头&#xff08;字节序&#xff09;* return 双字值*/ DWORD FsFileUtil::readAsD…

工欲善其事必先利其器——开发神器(IDEA)

&#x1f525;IDEA 简介&#x1f525;IDEA的特色功能&#x1f525;IDEA的优点&#x1f525;IDEA 的使用&#x1f525;IDEA的常用快捷键 &#x1f525;IDEA 简介 IDEA 全称 IntelliJ IDEA&#xff0c;是 java 编程语言开发的集成环境&#xff0c;被公认为最好的 java 开发工具之…

如何轻松搭建一套行情回放系统

一个量化策略在生产&#xff08;交易&#xff09;环境中运行时&#xff0c;实时数据的处理通常是由事件驱动的。为确保研发和生产使用同一套代码&#xff0c;通常在研发阶段需将历史数据&#xff0c;严格按照事件发生的时间顺序进行回放&#xff0c;以此模拟交易环境。在 Dolph…

ChatGPT 提问,软件杂项部分

堆内存与栈内存一般分别 有多少 ChatGPT 堆内存和栈内存的大小取决于操作系统和编译器的限制以及程序的运行环境。以下是一些常见的默认大小范围&#xff0c;但请注意这些值可以因环境而异&#xff1a; 栈内存大小&#xff1a; Windows平台&#xff1a;默认情况下&#xff…

vue3 大致总结

一、开发、生产、测试环境的文件编写 需要以VITE开头&#xff01;&#xff01;&#xff01; 输出时&#xff1a;console.log(import.meta.env.VITE_ENV,"------***---------"); 二、路由守卫 1、全局路由守卫beforeEach和afterEach ①全局前置守卫beforeEach ②…

六、达梦8数据库适配记录

达梦数据库适配记录 记录关于我的业务微服务,适配国产达梦数据库的过程,以及遇到的一些错误问题和其解决方案。 目前的项目最初基于Mysql开发,现在要适配到达梦,不要以为迁移任务很easy,但实际过程中还是出现了很多问题。 基 由于达梦是的国产数据库,本身与MySQL数据库…

idea配置阿里云翻译

idea配置阿里云翻译 0前言1开通阿里云机器翻译2配置阿里云AccessKeyidea配置Translation 0前言 使用idea的码农们都应该对Translation这款插件不会陌生了&#xff0c;尤其是英语基础比较薄弱的盆友&#xff0c;在看源码的时候更是会经常使用Translation边翻边看源码。 但是由于…

EW代理工具的使用说明

一、EW介绍 Earthworm&#xff08;EW&#xff09; 是一套便携式的网络穿透工具&#xff0c;具有 SOCKS v5服务架设和端口转发两大核心功能&#xff0c;可在复杂网络环境下完成网络穿透。 该工具能够以“正向”、“反向”、“多级级联”等方式打通一条网络隧道&#xff0c;直达…

基于REST风格的SpringMVC请求路径设置与参数传递

文章目录 1 REST简介2 RESTful入门案例2.1 环境准备2.2 思路分析2.3 修改RESTful风格新增删除传递路径参数 修改根据ID查询查询所有 知识点1&#xff1a;PathVariable 3 RESTful快速开发知识点1&#xff1a;RestController知识点2&#xff1a;GetMapping PostMapping PutMappin…

【STL】

目录 什么是STLSTL定义两大特点两个层次 STL主要构成容器容器概念容器分类vectordequestackqueuelistset/multiset容器map/multimap容器 算法迭代器仿函数适配器空间配置器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插…

选Ubuntu 还是 Fedora ?

提起开发&#xff0c;程序员们更青睐于不同版本的Linux操作系统而不是Windows。 为什么&#xff1f;因为Linux操作起来更安全、快捷&#xff0c;最重要的是&#xff0c;它的发行版本众多。你可以根据需要挑选最适合的那一款。那么&#xff0c;问题来了&#xff0c;到底哪个版本…

开源地质建模GemPy实战

推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 在设计任何类型的工程结构时&#xff0c;确定地面以下的东西并有效地将其映射出来是首要也是最重要的部分之一。 地下建模带有很大的误差范围&#xff0c;因为即使是我们今天使用的最先进的地下调查方法也无法完全绘制出…

【Linux】信号概述

目录 1、信号概念2、Linux常用信号表3、信号的5种默认处理动作 橙色 1、信号概念 信号是 Linux进程间通信的最古老的方式之一&#xff0c;是事件发生时对进程的通知机制&#xff0c;有时也称之为软件中断&#xff0c;它是在软件层次上对中断机制的一种模拟&#xff0c;是一种…

Vue3(6) Transition

目录 组件 基于CSS的过渡效果 JavaScript钩子 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被…

C++模板template

我们现在有几个变量&#xff0c;我们向要实现他们的交换&#xff0c;所以我们现在写了一个swap函数 我们现在可以实现对这两个变量之间的交换&#xff0c; 那么我们有有两个变量需要交换呢&#xff1f;&#xff1f; 我们刚才的Swap函数的参数是int类型的&#xff0c;我们现在的…