C++进阶6:红黑树原理
目录一、红黑树的概念1.1 红黑树的规则1.2 红黑树如何确保最长路径不超过最短路径的 2 倍1.3 红黑树的效率二、红黑树的实现2.1 红黑树的结构2.2 红黑树的插入2.2.1 红黑树插入一个值的大概过程2.2.2 情况 1变色2.2.3 情况 2单旋 变色2.2.4 情况 2双旋 变色2.4 红黑树的查找2.5 红黑树的验证一、红黑树的概念红黑树Red-Black Tree是一种自平衡的二叉搜索树它通过在插入和删除操作中维护一系列颜色规则确保树的高度始终保持在对数级别从而保证了高效的查找、插入和删除性能。1.1 红黑树的规则一棵红黑树必须满足以下 4 条核心性质每个节点要么是红色要么是黑色。根节点必须是黑色。如果一个节点是红色则它的两个子节点都是黑色即不能有两个连续的红色节点。从任一节点到其所有后代叶子节点的简单路径上均包含相同数量的黑色节点。注意这里的一条路径是从根节点一直走到NULL。1.2 红黑树如何确保最长路径不超过最短路径的 2 倍最短路径全为黑色节点的路径。最长路径红黑交替的路径。根据性质 4红色节点不能连续出现因此最长路径的长度不会超过最短路径的 2 倍。这保证了红黑树的平衡性其时间复杂度稳定在 O (log n)。1.3 红黑树的效率时间复杂度查找、插入、删除操作的平均和最坏时间复杂度均为 O (log n)。空间复杂度O (n)用于存储 n 个节点。与 AVL 树相比红黑树牺牲了部分平衡性换取了更少的旋转操作在频繁插入删除的场景下性能更优。二、红黑树的实现2.1 红黑树的结构在 C 中我们可以用结构体来定义红黑树的节点enum Colour { RED, BLACK }; templateclass K,class V struct RBTreeNode { pairK, V _kv; RBTreeNodeK, V* _left; RBTreeNodeK, V* _right; RBTreeNodeK, V* _parent; Colour _col; RBTreeNode(const pairK, V kv) :_kv(kv) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) {} }; templateclass K,class V class RBTree { typedef RBTreeNodeK, V Node; public: private: Node* _root nullptr; };2.2 红黑树的插入2.2.1 红黑树插入一个值的大概过程按照二叉搜索树的规则将新节点插入到合适的位置并将其颜色设为红色。(因为所有路径的黑色节点都必须保持一致如果插入的节点是黑色会非常麻烦)检查插入后是否违反了红黑树的性质。如果违反了性质则通过变色和旋转操作进行修复直到所有性质都得到满足。2.2.2 情况 1变色当新节点的父节点和叔叔节点都是红色时将父节点和叔叔节点变为黑色。将祖父节点变为红色。将祖父节点作为新的节点继续向上检查。cur作为新插入的节点这里只是一种概述情况但这种逻辑是只要节点出现这种情况就如此处理但我们需要注意如果我们变化的祖父节点是根节点就违反了红黑树的逻辑所以无论如何变化我们都要加上一句让根节点始终保持黑色。可以有两个连续的根节点2.2.3 情况 2单旋 变色当新节点的父节点是红色叔叔节点是黑色或者不存在。叔叔不存在则c一定是新增结点叔叔存在且为黑则 c一定不是新增c之前是黑色的是在c的⼦树中插入符合情况1变色将c从黑色变成红色更新上来的2.2.4 情况 2双旋 变色当新节点的父节点是红色叔叔节点是黑色或者不存在。叔叔不存在则c一定是新增结点叔叔存在且为黑则 c一定不是新增c之前是黑色的是在c的⼦树中插入符合情况1变色将c从黑色变成红色更新上来的2.3 红黑树的插入代码实现bool Insert(const pairK, V kv) { if (_root nullptr) { _root new Node(kv); _root-_col BLACK; return true; } Node* parent nullptr; Node* cur _root; while (cur) { if (cur-_kv.first kv.first) { parent cur; cur cur-_right; } else if (cur-_kv.first kv.first) { parent cur; cur cur-_left; } else { return false; } } cur new Node(kv); cur-_col RED; if (parent-_kv.first kv.first) { parent-_right cur; } else { parent-_left cur; } // 链接父亲 cur-_parent parent; // 父亲是红色出现连续的红色节点需要处理 while (parent parent-_col RED) { Node* grandfather parent-_parent; if (parent grandfather-_left) { // g // p u Node* uncle grandfather-_right; //情况1 if (uncle uncle-_col RED) { // 变色 parent-_col uncle-_col BLACK; grandfather-_col RED; // 继续往上处理 cur grandfather; parent cur-_parent; } else { //情况2 if (cur parent-_left) { // g // p u // c RotateR(grandfather); parent-_col BLACK; grandfather-_col RED; } else { //情况3 // g // p u // c RotateL(parent); RotateR(grandfather); cur-_col BLACK; grandfather-_col RED; } break; } } else { // g // u p Node* uncle grandfather-_left; // 叔叔存在且为红-》变色即可 //情况1 if (uncle uncle-_col RED) { parent-_col uncle-_col BLACK; grandfather-_col RED; // 继续往上处理 cur grandfather; parent cur-_parent; } else // 叔叔不存在或者存在且为黑 { // 情况二叔叔不存在或者存在且为黑 // 旋转变色 // g // u p // c //情况2 if (cur parent-_right) { RotateL(grandfather); parent-_col BLACK; grandfather-_col RED; } //情况3 else { RotateR(parent); RotateL(grandfather); cur-_col BLACK; grandfather-_col RED; } break; } } } _root-_col BLACK; return true; } void RotateR(Node* parent) { Node* subL parent-_left; Node* subLR subL-_right; parent-_left subLR; if (subLR) subLR-_parent parent; Node* pParent parent-_parent; subL-_right parent; parent-_parent subL; if (parent _root) { _root subL; subL-_parent nullptr; } else { if (pParent-_left parent) { pParent-_left subL; } else { pParent-_right subL; } subL-_parent pParent; } } void RotateL(Node* parent) { Node* subR parent-_right; Node* subRL subR-_left; parent-_right subRL; if (subRL) subRL-_parent parent; Node* parentParent parent-_parent; subR-_left parent; parent-_parent subR; if (parentParent nullptr) { _root subR; subR-_parent nullptr; } else { if (parent parentParent-_left) { parentParent-_left subR; } else { parentParent-_right subR; } subR-_parent parentParent; } }2.4 红黑树的查找红黑树的查找操作与普通二叉搜索树完全一致从根节点开始若查找值小于当前节点则向左子树查找若大于则向右子树查找。直到找到目标值或到达叶子节点NIL 节点。Node* Find(const K key) { Node* cur _root; while (cur) { if (cur-_kv.first key) { cur cur-_right; } else if (cur-_kv.first key) { cur cur-_left; } else { return cur; } } return nullptr; }2.5 红黑树的验证这里获取最长路径和最短路径检查最长路径不超过最短路径的2倍是不可行的因为就算满足这个条件红黑树也可能颜色不满足规则当前暂时没出问题后续继续插入还是会出问题的。所以我们还是去检查4点规则满足这4点规则⼀定能保证最长路径不超过最短路径的2倍。1. 规则1枚举颜色类型天然实现保证了颜色不是黑色就是红色2. 规则2直接检查根即可3. 规则3前序遍历检查遇到红色结点查孩子不太方便因为孩子有两个且不一定存在反过来检查父亲的颜色就方便多了4. 规则4前序遍历遍历过程中用形参记录跟到当前结点的blackNum(黑色节点数量)前序遍历遇到黑色结点就blackNum走到空就计算出了一条路径的黑色结点数量。再任意一条路径黑色结点数量作为参考值依次比较即可。bool IsBalance() { if (_root nullptr) return true; if (_root-_col RED) return false; // 参考值 int refNum 0; Node* cur _root; while (cur) { if (cur-_col BLACK) { refNum; } cur cur-_left; } return Check(_root, 0, refNum); } private: bool Check(Node* root, int blackNum, const int refNum) { if (root nullptr) { // 前序遍历走到空时意味着一条路径走完了 //cout blackNum endl; if (refNum ! blackNum) { cout 存在黑色结点的数量不相等的路径 endl; return false; } return true; } // 检查孩子不太方便因为孩子有两个且不一定存在反过来检查父亲就方便多了 if (root-_col RED root-_parent-_col RED) { cout root-_kv.first 存在连续的红色结点 endl; return false; } if (root-_col BLACK) { blackNum; } return Check(root-_left, blackNum, refNum) Check(root-_right, blackNum, refNum); }

相关新闻

go lo-slice使用示例

go lo-slice使用示例

使用例子 包含了以下几个主要部分:基本切片操作:Filter - 过滤切片元素Map - 转换切片元素Reduce - 归约切片元素ForEach - 遍历切片元素切片转换操作:SliceToMap - 将切片转换为映射KeyBy - 按键分组FilterMap - 过滤并转换FlatMap - 扁平化…

2026/7/5 18:56:41 阅读更多 →
Flutter 三方库 steel_crypt 的鸿蒙化适配指南 - 掌控加密算法资产、精密安全治理实战、鸿蒙级防护专家

Flutter 三方库 steel_crypt 的鸿蒙化适配指南 - 掌控加密算法资产、精密安全治理实战、鸿蒙级防护专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net Flutter 三方库 steel_crypt 的鸿蒙化适配指南 - 掌控加密算法资产、精密安全治理实战、鸿蒙级防护专家 在鸿蒙跨平台应用执行高级安全管理与多维加密算法资产指控(如构建一个支…

2026/5/17 9:30:19 阅读更多 →
利用AI写专著,这些工具你不能错过!快速完成学术专著

利用AI写专著,这些工具你不能错过!快速完成学术专著

学术专著的写作,不仅是对一个人学术能力的严格考验,也是一种心理承受力的挑战。与团队合作的论文写作不同,专著的创作大多是独立进行的。从选题、构建框架到撰写内容、进行修改,几乎每一个环节都需要研究者自己完成。长期的单独创…

2026/7/3 2:36:08 阅读更多 →

最新新闻

锂离子电池电量精准估算技术与PIC单片机应用

锂离子电池电量精准估算技术与PIC单片机应用

1. 锂离子电池电量估算的挑战与解决方案在便携式电子设备设计中,最令人头疼的问题之一就是准确估算剩余电池电量。我经历过无数次用户抱怨"明明显示还有30%电量,怎么突然就关机了"的场景。传统电压测量法在锂离子电池上误差可达20%以上&#x…

2026/7/6 11:17:42 阅读更多 →
淘宝新手商家运营难题拆解!低成本破局引流实战方案

淘宝新手商家运营难题拆解!低成本破局引流实战方案

很多淘宝新手商家开店后都会陷入同一个困境:开店容易起店难,有商品、没流量,有访客、没转化,花钱推广没效果,免费流量拿不到。不少新手盲目跟风开直通车、淘客通用推广,烧钱无数却销量惨淡,甚至…

2026/7/6 11:15:38 阅读更多 →
终于找到不套路、能长期托付的社区宠物店✨​

终于找到不套路、能长期托付的社区宠物店✨​

住在山水黔城周边的养宠家庭,应该都有同款烦恼:家门口的宠物店看着不少,但想找一家靠谱、不套路、能长期放心托付的真的太少了。​贵阳周边很多门店服务质量参差不齐,有的只做最简单的流水线洗护,敷衍了事;…

2026/7/6 11:13:34 阅读更多 →
Plone主题开发实战:Theme Package架构与Resource Registry深度指南

Plone主题开发实战:Theme Package架构与Resource Registry深度指南

1. 项目概述:为什么一个Plone主题启动指南值得花时间细读 Plone主题开发不是“换个CSS颜色”那么简单的事——它是一套融合了Zope组件架构、TAL模板语言、资源注册机制与前端构建流程的完整工作流。如果你刚接触Plone,或者从5.x升级到6.x,会发…

2026/7/6 11:11:29 阅读更多 →
CNN 过拟合实战:PyTorch 2.0 中 5 种正则化方法对比与调优

CNN 过拟合实战:PyTorch 2.0 中 5 种正则化方法对比与调优

CNN 过拟合实战:PyTorch 2.0 中 5 种正则化方法对比与调优 当你在训练一个卷积神经网络(CNN)时,最令人沮丧的瞬间莫过于看到训练准确率节节攀升,而验证集表现却停滞不前——这就是典型的过拟合现象。在图像分类任务中&…

2026/7/6 11:09:24 阅读更多 →
Python字典底层原理:哈希表、开放寻址与有序性实现

Python字典底层原理:哈希表、开放寻址与有序性实现

1. 项目概述:为什么一个“普通”的字典,值得你花一整个下午去真正搞懂? 你有没有过这种经历:写了一段看似完美的 Python 代码,处理几万条用户数据时飞快,可一旦数据量涨到百万级,程序就突然卡在…

2026/7/6 11:09:24 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻