C++分析红黑树
目录
红黑树介绍
红黑树的性质与平衡控制关系
红黑树节点的插入
情况1:不需要调整
情况2:uncle节点为红色
情况3:uncle节点为黑色
总结与代码实现
红黑树的删除(待实现)
红黑树的效率
红黑树介绍
红黑树是第二种平衡二叉搜索树,为了解决普通二叉搜索树的效率问题,红黑树在控制平衡时选择近似平衡而不是AVL树的绝对平衡,每一个节点都会存储一个表示颜色的属性,包括红色和黑色,通过对颜色的控制,红黑树可以保证没有一条路径会比其他路径的长度长出两倍
红黑树的性质
- 根节点一定是黑色
- 一条简单路径下不会出现连续的红色节点,如果父亲节点为红色,则其孩子节点一定为黑色,如果父亲节点为黑色,则没有限制
- 对于每个节点来说,从该节点开始到其后代所有节点的简单路径上均包含相同数量的黑色节点
- (了解)每个空叶子结点都是黑色的
在上图中,NIL表示空叶子节点,以NIL作为结束条件,一共有11条路径,需要注意不是以叶子节点为结束条件(即不是7条路径)
满足上面的四个性质,红黑树就可以保证其相对平衡的条件:红黑树可以保证最长路径会比最短路径的长度长出两倍。
最短路径:节点颜色为全黑的路径,此时黑色节点的个数即为路径的长度
最长路径:节点颜色为红色和黑色交替出现的路径
红黑树的性质与平衡控制关系
因为黑色节点的个数可以决定一条路径的长度,假设黑色节点的个数为h,则最短路径的长度也为h,满足第二条规则时,红色节点的个数不会超过黑色节点(1个或者h个),从而最长路径的最大长度为2h,因为红色节点是在黑色节点出现后才会出现。
如果按照下图的插入方式导致红色节点连续出现:
违反了规则二,此时最短路径的长度的两倍会小于最长路径的长度,从而打破了红黑树的平衡。
如果插入的27为黑色节点,则最长路径的长度增加,并且黑色节点的个数也增加,违反了规则三,此时对于其他路径来说也需要增加一个黑色节点。
所以为了维持平衡,不可以插入黑色节点,此时最长路径的长度刚好为2h,如下图所示:
综上所述,在满足第二条规则和第三条规则下可以保证红黑树的最长路径始终不会超过最短路径的2倍
红黑树节点的插入
根据红黑树的性质可以看出红黑树如何控制高度近似平衡,但是如果插入了节点,可能会破坏原有的平衡,此时需要通过重新填色或者旋转调整使红黑树重新达到平衡
在前面的分析中可以得知,如果插入的节点是黑色节点,可能会导致每一条路径都需要多一个黑色节点,为了更加方便处理,规定插入的节点是红色节点
情况1:不需要调整
如果插入的节点是红色节点,并且其父亲节点是黑色节点,此时不需要进行任何处理,当父亲节点是黑色节点时,保证了规则三没有违背,因为黑色节点的个数决定了高度,插入前如果保证原树是红黑树,那么高度一定满足红黑树的近似平衡,并且此时插入红色节点也不会违背规则二,如下图的一种情况所示:
情况2:uncle
节点为红色
如果cur
的节点是红色,并且其父亲节点(假设为parent
)是红色节点,此时说明父亲的兄弟节点(假设为uncle
)也一定为红色,因为插入前一定是红黑树(插入前不是红黑树那么插入前就已经出现了不平衡,需要进行调整),当父亲节点时红色,说明父亲节点所在的路径缺少黑色节点,违反了规则三。父亲节点的父亲节点(假设为grandfather
)也一定为黑色,如果为红色则违反了第二条规则所以插入前的状态应该为:
cur
节点可能为新增的红色节点,也可能为上一次调整变为的红色节点
- 假设a、b、c、d和e为黑色节点个数为0的红黑树,此时
cur
为新插入节点如下图所示:
因为出现了连续的红色节点,为了恢复红黑树的平衡,此时需要进行调整,因为uncle
节点为红色,为了保证每条路径上都有一个黑色节点并且保证没有连续的红色节点出现,将parent
节点的颜色改为黑色,将uncle
节点的颜色改为黑色,将grandfather
节点改为红色(如果grandfather
节点为根节点则再处理为黑色),处理完后,cur = grandfather
继续向上调整直到遇到根节点:
对于uncle
为红色的情况来说,不需要考虑插入位置在parent
左或者右、parent
在grandfather
的左或者右已经uncle
在grandfather
的左或者右,因为不论是哪种情况,本质都是将uncle
和parent
变为黑色增加两边路径的黑色节点使其满足规则二和规则三
- 假设a、b、c、d和e为黑色节点个数大于0,此时
cur
为上一次调整变成的红色节点,如下图所示:
对于当前情况来说,只有cur
位置的节点是红色,其余几棵子树已经通过调整变成了符合规则的红黑树,所以也可以归类为上面的情形,处理方式与上面相同
情况3:uncle
节点为黑色
uncle
节点为黑色时一共有两种情况:
uncle
节点不存在uncle
节点存在且为黑
因为红黑树规定下空节点是黑色的,所以uncle
节点不存在与存在且为黑可以视为一种情况,下面主要以uncle
节点不存在进行分析,对于uncle
节点存在且为黑的情况给出一种分析,剩下与uncle
节点不存在的情况类似
下面分析中,uncle
节点不存在时将不展示NIL
节点
当cur
节点在parent
的右子树并且parent
在grandfather
的右子树时,并且uncle
不存在时,此时需要进行左单旋,将parent
的颜色更新为黑色,grandfather
的颜色更新为红色,因为如果仅仅是将parent
变成黑色,则依旧不满足规则三,其余路径还是少一个黑色节点,通过左单旋使得当前子树的根节点变为黑色时可以使当前根节点出发的所有路径都至少有一个黑色节点,如下图所示:
当cur
节点在parent
的左子树并且parent
在grandfather
的左子树时,此时需要进行右单旋,将parent
的颜色更新为黑色,grandfather
的颜色更新为红色,原因类比左单旋,过程如下图所示:
当cur
节点在parent
的右子树并且parent
在grandfather
的左子树时,此时需要进行右左双旋,将cur
的颜色更新为黑色,将grandfather
的颜色更新为红色,过程如下:
当cur
节点在parent
的左子树并且parent
在grandfather
的右子树时,此时需要进行左右双旋,将cur
的颜色更新为黑色,将grandfather
的颜色更新为红色,过程如下:
如果uncle
节点本身存在,那么经过uncle
节点的路径下方的两个子树为红色,而parent
插入节点前至少会有一个黑色节点,如下图所示:
此时在parent
的右侧插入一个cur
节点(本身就是红色节点cur
节点也是如此)如下:
此时如果只是对parent
节点的颜色进行改变,则会出现parent
所在路径比uncle
所在路径多一个黑色节点,所以为了解决这个问题,单单改变颜色无法解决,当cur
在parent
的右边时,只需要一次左单旋即可,而cur
在parent
的左边时,需要先进行右旋再进行左旋,以右左双旋为例,如下图所示:
对于其他两种情况也是一样的道理,此处不再赘述
总结与代码实现
红黑树的插入过程一共有三种情况:
- 当插入节点的父亲节点为黑色时,直接插入节点即可,不需要进行任何调整
- 当uncle节点为红色时,
cur
节点的parent
节点和uncle
节点均变为黑色,将所在子树的根节点变为红色,如果子树的根节点为整棵树的根节点,则再处理为黑色 - 当uncle节点为黑色(包括空节点的黑色和存在且为黑色节点)时,需要进行旋转
parent
和cur
节点是同方向时,进行一次旋转(左旋或者右旋),将parent
节点变为黑色,将grandfather
节点变为红色,此时因为parent是本棵子树的根,所以更新完后当前子树也是一棵红黑树,颜色是黑色不需要再进行调整parent
和cur
节点不是同方向时,进行两次旋转(先左后右或者先右后左),再将cur
节点变为黑色,grandfather
节点变为红色,更新完后当前子树也是一棵红黑树,并且因为cur
的颜色是黑色不需要再进行调整
代码实现:
// 树插入
bool insertNode(const pair<T, V>& kv)
{// 判断key是否已经存在if (findNode(kv.first)){// 已经存在时插入失败return false;}// 判断根节点是否为空,为空则作为第一个节点if (_root == nullptr){_root = new node(kv);return true;}// 根节点不为空时接着遍历插入node* cur = _root;// 定义父亲节点记录父亲的位置node* parent = _root;while (cur){if (kv.first > cur->_kv.first){// 大于根节点数据,插入在右子树,走到左子树的空位置parent = cur;cur = cur->_right;}else if (kv.first < cur->_kv.first){// 小于根节点数据,插入在左子树,走到右子树的空位置parent = cur;cur = cur->_left;}}// 创建node节点// 创建节点可以使用新节点,但是要使cur走到新节点的位置,便于下面判断新节点的平衡因子,再通过cur判断祖先节点的平衡因子cur = new node(kv);// parent为叶子节点,链接新节点if (parent->_kv.first < kv.first){// 如果是右子树,则插入在右子树parent->_right = cur;}else{// 否则插入在左子树parent->_left = cur;}// 链接父亲cur->_parent = parent;// 插入节点颜色为红色cur->_col = Red;// 存在父亲且当父亲节点为红色时需要判断是否更新while (parent && parent->_parent && parent->_col == Red){node* grandfather = parent->_parent;if (grandfather->_right == parent){node* uncle = grandfather->_left;if (uncle && uncle->_col == Red){// 叔叔存在且为红uncle->_col = parent->_col = Black;grandfather->_col = Red;cur = grandfather;parent = cur->_parent;}else{// cur在parent的右孩子if (cur == parent->_right){// 右右->左单旋rotateLeft(grandfather);// 改变颜色grandfather->_col = Red;parent->_col = Black;}else{// 右左->右左双旋rotateRight(parent);rotateLeft(grandfather);cur->_col = Black;grandfather->_col = Red;}// 旋转结束后不需要再向上更新break;}}else{node* uncle = grandfather->_right;if (uncle && uncle->_col == Red){// 叔叔存在且为红uncle->_col = parent->_col = Black;grandfather->_col = Red;// 继续向上更新cur = grandfather;parent = cur->_parent;}else{if(cur == parent->_left){// 叔叔不存在// 左左->右单旋rotateRight(grandfather);// 改变颜色grandfather->_col = Red;parent->_col = Black;}else{// 左右->左右双旋rotateLeft(parent);rotateRight(grandfather);// 改变颜色grandfather->_col = Red;cur->_col = Black;}// 旋转结束后不需要再向上更新break;}}}// 根节点为黑色_root->_col = Black;return true;
}
红黑树的删除(待实现)
红黑树的效率
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(),红黑树不追
求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,
所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红
黑树更多
相关文章:

C++分析红黑树
目录 红黑树介绍 红黑树的性质与平衡控制关系 红黑树节点的插入 情况1:不需要调整 情况2:uncle节点为红色 情况3:uncle节点为黑色 总结与代码实现 红黑树的删除(待实现) 红黑树的效率 红黑树介绍 红黑树是第二种平衡二…...

mysql线上查询之前要性能调优
查询优化是数据库性能调优的关键方面,目的是减少查询的执行时间和资源消耗。以下是一些常见的查询优化技巧及其示例: 使用合适的索引 问题: 全表扫描导致查询缓慢优化: 为经常用于搜索条件的列添加索引示例: 假设有一…...

GPIO输出控制之LED闪烁、LED流水灯以及蜂鸣器应用案例
系列文章目录 STM32之GPIO(General Purpose Input/Output,通用型输入输出) 文章目录 系列文章目录前言一、LED和蜂鸣器简介1.1 LED1.2 蜂鸣器1.3 面包板 二、LED硬件电路2.1 低电平驱动电路2.2 高电平驱动电路 三、蜂鸣器硬件电路3.1 PNP型三…...

体系结构论文导读(三十四):Design of Reliable DNN Accelerator with Un-reliable ReRAM
文章核心 这篇文章主要讨论了一种在不可靠的ReRAM(阻变存储器)设备上设计可靠的深度神经网络(DNN)加速器的方法。文章提出了两种关键技术来解决ReRAM固有的不可靠性问题:动态定点(DFP)数据表示…...

WebStock会话
其实使用消息队列也可以实现会话,直接前端监听指定的队列,使用rabbitmq的分组还可以实现不同群聊的效果。 1、依赖搭建: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org…...

5_现有网络模型的使用
教程:现有网络模型的使用及修改_哔哩哔哩_bilibili 官方网址:https://pytorch.org/vision/stable/models.html#classification 初识网络模型 pytorch为我们提供了许多已经构造好的网络模型,我们只要将它们加载进来,就可以直接使…...

软件安全测试报告内容和作用简析,软件测试服务供应商推荐
在数字化时代,软件安全问题愈发凸显,安全测试显得尤为重要。软件安全测试报告是对软件系统在安全性方面进行评估和分析后的书面文件。该报告通常包含测试过程、测试发现、漏洞描述、风险评估及改进建议等重要信息。报告的目的是为了帮助开发团队及时发现…...

算法板子:树形DP、树的DFS——树的重心
思想: 代码: #include <iostream> #include <cstring> using namespace std;const int N 1e5 10;// vis标记当前节点是否被访问过; vis[1]true代表编号为1的节点被访问过 bool vis[N]; // h数组为邻接表; h数组上的每个坑位都串了一个单链…...

在C语言中,联合体或共用体(union )是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。
在C语言中,union 是一种特殊的数据类型,允许在相同的内存位置存储不同的数据类型。这意味着 union 中的所有成员共享同一块内存空间,因此它们之间会相互覆盖。在你给出的 Acceleration_type union 定义中,包含了三种不同类型的成员…...

MS2201以太网收发电路
MS2201 是吉比特以太网收发器电路,可以实现超高速度的 全双工数据传输。它的通信遵从 IEEE 802.3 Gigabit Ethernet 协议 中的 10 比特接口的时序要求协议。 MS2201 支持数据传输速率从 1Gbps 到 1.85Gbps 。 主要特点 ◼ 电源电压: 2.5V 、 3.3V …...

乐乐音乐Kotlin版
简介 乐乐音乐Kotlin版,主要是基于ExoPlayer框架开发的Android音乐播放器,它支持lrc歌词和动感歌词(ksc歌词、krc歌词、trc歌词、zrce歌词和hrc歌词等)、多种格式歌词转换器及制作动感歌词、翻译歌词和音译歌词。 编译环境 Android Studio Jellyfish | …...

C语言——预处理和指针
C语言——预处理和指针 预处理宏宏定义宏的作用域带参的宏 文件包含条件编译 指针指针的概念指针的定义指针变量初始化指针一维整型数组 预处理 编程的流程分为:编辑、编译、运行、调试四个阶段; 预处理属于编译阶段,编译过程又可以分为&…...

iptables防火墙(一)
目录 1、Linux防火墙基础 2、iptables的四表五链结构 2.1 iptables的四表五链结构介绍 2.2 四表五链 2.2.1 四表 2.2.2 五链 2.3 包过滤的匹配流程 2.3.1 规则链之间匹配顺序 2.3.2 规则链内部的处理规则 2.3.3 数据包过滤的匹配流程 3、 编写防火墙规则 3.1 iptabe…...

(leetcode学习)50. Pow(x, n)
实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000示例 2: 输入:x 2.10000, n 3 输出:9.26100示例 …...

QT 5.12.0 for Windows 安装包 QT静态库 采用源码静态编译生成
qt-5.12.0-static.zip 下载地址(资源整理不易,下载使用需付费,且文件较大,不能接受请勿浪费时间下载): 链接:https://pan.baidu.com/s/1ftfHFG_jGFwVaOAvBVrNFg?pwdtvtp 提取码:tvtp...

【生成式人工智能-三-promote 神奇咒语RL增强式学习RAG】
如何激发模型的能力 提示词 promotCoTRL 增强式学习Reforcement learning提供更多的资料提供一些范例Incontext- learning 任务拆解让模型自己检查错误让模型多次生成答案Tree of Thoughts让模型使用其他工具RAG写程序POT其他工具 让多个模型合作参考 在模型不变的情况下&#…...

C++连接oracle数据库连接字符串
//远程连接,需要安装oracle客户端sprintf(szConnect4, ("Provider OraOLEDB.Oracle.1; Password %s; Persist Security Info True; User ID %s; Data Source \"(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST %s)(PORT 1521)) )(CONN…...

判断字符串是否接近:深入解析及优化【字符串、哈希表、优化过程】
本文将详细解析解决这个问题的思路,并逐步优化实现方案。 问题描述 给定两个字符串 word1 和 word2,如果通过以下操作可以将 word1 转换为 word2,则认为它们是接近的: 交换任意两个现有字符。将一个现有字符的每次出现转换为另…...

C 和 C++ 中信号处理简单介绍
信号处理是编程中一个重要的主题,特别是在需要处理异步事件和错误情况的系统中。在 C 和 C 语言中,信号处理机制提供了一种优雅的方式来响应特定的系统事件,例如用户中断、异常情况或其他信号。在这里,我将详细介绍 C 和 C 中信号…...

什么是云边协同?
当今信息技术高速发展的时代,"云边协同"(Edge Cloud Collaboration)已经成为一个备受关注的话题。它涉及到云计算和边缘计算的结合,为数据处理、存储和应用提供了全新的可能性。本文将介绍云边协同的概念、优势以及在不…...

YOLOv5改进 | 主干网络 | 将backbone替换为MobileNetV2【小白必备教程+附完整代码】
秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 💡💡💡本专栏所有程序均经过测试,可成功执行💡💡💡 专栏目录: 《YOLOv5入门 改…...

ARMxy边缘计算网关用于过程控制子系统
在现代工业生产中,过程控制系统的优化对于提高生产效率、保证产品质量、降低能源消耗等方面都具有重要意义。而 ARMxy 工控机作为一种高性能、高可靠性的工业控制设备,正逐渐成为过程控制系统优化的新选择。 ARMxy 工控机采用了先进的 ARM 架构处理器&am…...

Python | TypeError: unsupported operand type(s) for +=: ‘int’ and ‘str’
Python | TypeError: unsupported operand type(s) for : ‘int’ and ‘str’:深度解析 在Python编程中,遇到“TypeError: unsupported operand type(s) for : ‘int’ and ‘str’”这类错误通常意味着你尝试将一个整数(int)和…...

什么是开源什么是闭源?以及它们之间的关系
开源软件(Open Source Software) 定义:开源软件是指其源代码可以被公众访问和使用的软件。用户可以查看、修改和增强软件的源代码。 许可:通常遵循特定的开源许可证,如GNU通用公共许可证(GPL)、…...

SpringBoot+Mybatis Plus实际开发中的注解
SpringBoot+Mybatis Plus实际开发中的注解 实体类Service层Mapper层Controller层启动类配置类SpringBoot+Mybatis Plus实际开发中的注解 实体类 @Data : 底层实现了getter、setter、toString、hashCode、equals 和无参构造@AllArgsConstructor: 底层实现了有参构造@NoArgsCon…...

【香橙派系列教程】(八)一小时速通Python
【八】一小时速通Python 本章内容服务于香橙派下的开发,用C语言的视角来学习即可,会改就行。 详细说明,请看链接:python全篇教学 Python是一种动态解释型的编程语言,Python可以在Windows、UNIX、MAC等多种操作系统上 使用&…...

了解JavaScript 作用、历史和转变
JavaScript 是一种即时执行的脚本语言,其代码在浏览器环境中通过内置的 JavaScript 引擎被动态地一行接一行地解释执行。这一特性赋予了开发者极高的灵活性和效率,因为代码修改后能立即生效,无需经历编译过程,从而加速了开发周期和…...

遗传算法与深度学习实战——生命模拟与进化论
遗传算法与深度学习实战——生命模拟与进化论 0. 前言1. 模拟进化1.1 代码实现1.2 代码改进 2. 达尔文进化论3. 自然选择和适者生存3.1 适者生存3.2 进化计算中的生物学 小结系列链接 0. 前言 生命模拟通过计算机模拟生物体的基本特征、遗传机制、环境互动等,试图模…...

rt-thread H7 使用fdcan没有外接设备时或发送错误时线程被挂起的解决方案
一、问题查找 使用的开发版是硬石的H7芯片型号STM32H743IIT6,测试时发现如果外面没有连接CAN设备,程序调用CAN发送时会一直等待发送反馈,导致相关线程挂起。 在线仿真时发现是卡在can.c文件的168行_can_int_tx函数:rt_co…...

exptern “C“的作用,在 C 和 CPP 中分别调用 openblas 中的 gemm 为例
openblas提供的sgemm有两种方式,一种是通过cblas,另一种是直接声明并调用 sgemm_ 其中,cblas方式是更正规调用方法; 1,调用openblas的 sgemm 的两种方式 1.1 c语言程序中使用 sgemm hello_sgemm.c #include <st…...