【C++】红黑树的模拟实现
🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘。
🛸C++专栏:C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!
一、红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

二、红黑树的性质
-
每个结点不是红色就是黑色
-
根节点是黑色的
-
如果一个节点是红色的,则它的两个孩子结点是黑色的
-
对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
-
每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN
最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。
可以发现,最坏情况的时间复杂度和AVL树一样,都是O(logN),但是红黑树这种近似平衡的结构减少了大量旋转,综合性能优于AVL树。
注:第三点的意思就是,没有连续的红色节点进行连接
三、红黑树的定义
enum Color
{RED,BLACK
};
template<class K, class V>
struct RedBlackTreeNode
{pair<K, V> _kv;RedBlackTreeNode<K, V>* _left;//该节点的左孩子RedBlackTreeNode<K, V>* _right;//该节点的右孩子RedBlackTreeNode<K, V>* _parent;//该节点是父亲节点Color _col;//颜色RedBlackTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr),_col(RED){}
};
思考:在节点的定义中,为什么要将节点的默认颜色给为红色的而不是黑色?
因为给成红色就会和红黑树的性质3冲突,而给成黑色就会和红黑树的性质4冲突那么对于冲突性质3比性质4更优,因为冲突性质4,不管插入哪个位置,都会引起颜色的变换或者旋转。而冲突性质3有可能会引起改变,也可能不改变
四、红黑树的插入(主要看叔叔的颜色)
1.情况一:uncle存在且节点颜色为红
这种情况cur、parent、grandfather都是确定颜色的,唯独uncle的颜色是不确定的。

2.情况二:uncle不存在或者uncle存在且节点为黑(直线)
uncle不存在示例图:

uncle存在且为黑的情况示例图:

3.情况三:uncle不存在/存在并且为黑(折线)
uncle的情况分两种。
uncle不存在,则cur为插入节点,两次单旋即可。

uncle存在且为黑示例图

4.总结
插入新节点时,父节点为红,看叔叔的颜色。
1、叔叔存在且为红,变色,向上调整(可能变为三种情况中的任意一种)
2、叔叔不存在/存在且为黑,直线。单旋+变色
3、叔叔不存在/存在且为黑,折线,两次单旋+变色
五、红黑树的插入代码
bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;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);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// ... 控制平衡while (parent && parent->_col == RED)//parent不为空并且为红进循环{Node* grandfather = parent->_parent;if (grandfather->_left == parent){if (parent->_left == cur){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔存在并且叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}else{if (parent->_right == cur){Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}}_root->_col = BLACK;//处理根一直为黑的情况return true;}
六、红黑树代码是否正确的代码检测
bool checkColour(Node* root, int blacknum, int beachmark){if (root == nullptr){if (blacknum != beachmark)//和基准值比较,如果不相等,则红黑树代码出错{return false;}return true;}if (root->_col == BLACK)//记录黑色节点数量{++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return checkColour(root->_left, blacknum, beachmark) && checkColour(root->_right, blacknum, beachmark);}bool _IsBalance(Node* root){if (root == nullptr){return true;}if (root->_col != BLACK)//根节点不为黑,不符合红黑树的性质{return false;}//基准值int beanchmark = 0;Node* cur = root;while (cur)//求一条路径的黑色节点的数量作为基准值{if (cur->_col == BLACK){++beanchmark;}cur = cur->_left;}return checkColour(root, 0, beanchmark);}
详看代码注释
七、红黑树的整体代码
#include <iostream>
#include <cassert>
using namespace std;template<class K, class V>
class RedBlackTree
{typedef RedBlackTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;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);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;// ... 控制平衡while (parent && parent->_col == RED)//parent不为空并且为红进循环{Node* grandfather = parent->_parent;if (grandfather->_left == parent){if (parent->_left == cur){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//叔叔存在并且叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}else{if (parent->_right == cur){Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔节点为红{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else //叔叔节点为空或者为黑的情况{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}}_root->_col = BLACK;//处理根一直为黑的情况return true;}bool IsBalance(){return _IsBalance(_root);}
private:bool checkColour(Node* root, int blacknum, int beachmark){if (root == nullptr){if (blacknum != beachmark){return false;}return true;}if (root->_col == BLACK){++blacknum;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << root->_kv.first << "出现连续红色节点" << endl;return false;}return checkColour(root->_left, blacknum, beachmark) && checkColour(root->_right, blacknum, beachmark);}bool _IsBalance(Node* root){if (root == nullptr){return true;}if (root->_col != BLACK){return false;}//基准值int beanchmark = 0;Node* cur = root;while (cur){if (cur->_col == BLACK){++beanchmark;}cur = cur->_left;}return checkColour(root, 0, beanchmark);}void RotateR(Node* parent){Node* cur = parent->_left;Node* curRight = cur->_right;parent->_left = curRight;cur->_right = parent;Node* ppNode = parent->_parent;if (curRight){curRight->_parent = parent;}parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = cur;}else{ppNode->_right = cur;}cur->_parent = ppNode;}}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;parent->_right = curleft;if (curleft)//判断是否为空,空的话就不用接上父亲节点{curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}}
private:Node* _root = nullptr;
};
相关文章:
【C++】红黑树的模拟实现
🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风…...
【多线程】Thread 类 详解
Thread 类 详解 一. 创建线程1. 继承 Thread 类2. 实现 Runnable 接口3. 其他变形4. 多线程的优势-增加运行速度 二. Thread 类1. 构造方法2. 常见属性3. 启动线程-start()4. 中断线程-interrupt()5. 线程等待-join()6. 线程休眠-sleep()7. 获取当前线程引用 三. 线程的状态1. …...
LINUX 网络管理
目录 一、NetworkManager的特点 二、配置网络 1、使用ip命令临时配置 1)查看网卡在网络层的配置信息 2)查看网卡在数据链路层的配置信息 3)添加或者删除临时的网卡 4)禁用和启动指定网卡 2、修改配置文件 3、nmcli命令行…...
refresh rate
1920 x 1080 显卡刷新率 60...
使用 NGINX Unit 实施应用隔离
原文作者:Artem Konev - Senior Technical Writer 原文链接:使用 NGINX Unit 实施应用隔离 转载来源:NGINX 中文官网 NGINX 唯一中文官方社区 ,尽在 nginx.org.cn NGINX Unit 特性集的最新动态之一是支持应用隔离,该特…...
2023/09/12 qtc++
实现一个图形类(Shape) ,包含受保护成员属性:周长、面积, 公共成员函数:特殊成员函数书写 定义一个圆形类(Circle) ,继承自图形类,包含私有属性:半径 公共成员函数:特殊成员函数…...
全科医学科常用评估量表汇总,建议收藏!
根据全科医学科医生的量表使用情况,笔者整理了10个常用的全科医学科量表,可在线评测直接出结果,可转发使用,可生成二维码使用,可创建项目进行数据管理,有需要的小伙伴赶紧收藏! 日常生活能力量表…...
了解消息中间件的基础知识
为什么要使用消息中间件? 解耦:消息中间件可以使不同的应用程序通过解耦的方式进行通信,减少系统间的依赖关系提供异步通信:消息中间件可以实现异步消息传递,提高系统的响应性能。流量削峰:消息中间件可以…...
【linux】Linux wps字体缺失、加粗乱码解决
解决wps字体缺失问题 1、下载字体包 git clone https://github.com/iamdh4/ttf-wps-fonts.git2、创建单独放置字体的目录 mkdir /usr/share/fonts/wps-fonts3、复制字体到系统目录下 cp ttf-wps-fonts/* /usr/share/fonts/wps-fonts4、修改字体权限 chmod 644 /usr/share/f…...
每日两题 103二叉树的锯齿形层序遍历(数组) 513找树左下角的值(队列)
103 题目 103 给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。 示例 1: 输入:root [3,9,…...
ROS2报错:ImportError: cannot import name ‘Log‘ from ‘rosgraph_msgs.msg‘
在使用ros2的bag命令查看数据集信息时报错 Traceback (most recent call last):File "/opt/ros/noetic/bin/rosbag", line 34, in <module>import rosbagFile "/opt/ros/noetic/lib/python3/dist-packages/rosbag/__init__.py", line 33, in <mo…...
【Vue】Vue中的代码分为哪几种类型?
在 Vue 中的代码可以分为以下几种类型: 1.模板代码 模板代码是 Vue 中用来生成 HTML 的一种语法,可以通过 Vue 的模板语法和指令来动态渲染页面。模板代码一般写在 Vue 组件的 template 标签中。 2.JavaScript 代码 JavaScript 代码是 Vue 组件中用来…...
es6中includes用法
js中的includes用法 1.数组 includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false [a,b,c].includes(a) true [a,b,c].includes(1) false includes可以包含两个参数,第二个参数表示判断的起始位置 起始位置第一个数字是0。 2.字符串 …...
QT中QRadioButton实现分组C++
通过对QRadioButton组件进行分组可解决QRadioButton组件的互斥性 实现如下。 假设已设计好UI并且有UI代码情况: 头文件引用: #include <QButtonGroup> 分组功能 ,cpp文件代码实现: Your_Project::Your_Project(QWidge…...
kafka实战报错解决问题
需求 在一个在线商城中,用户下单后需要进行订单的处理。为了提高订单处理的效率和可靠性,我们使用Kafka来实现订单消息的异步处理。当用户下单后,订单信息会被发送到Kafka的一个Topic中,然后订单处理系统会从该Topic中消费订单消…...
vite+react 使用 react-activation 实现缓存页面
对应的版本 "react": "^18.2.0", "react-activation": "^0.12.4", "react-dom": "^18.2.0", "react-router-dom": "^6.15.0",react-activation 这是一个npm包,在react keep alive…...
【android 蓝牙开发——蓝牙耳机】
【android 蓝牙开发——传统蓝牙】 【android 蓝牙开发——BLE(低功耗)蓝牙 2021-10-09更新】 总结一下蓝牙开发的基本使用以及蓝牙耳机的断开和链接。 所以需权限: <uses-permission android:name"android.permission.ACCESS_FIN…...
Golang goroutine 进程、线程、并发、并行
goroutine 看一个需求 需求:要求统计1-200000000000的数字中,哪些是素数? 分析思路: 1)传统的方法,就是使用一个循环,循环的判断各个数是不是素数(一个任务就分配给一个cpu去做,这样很不划算…...
如何做到安全上网
随着信息化的发展,企业日常办公越来越依赖互联网,而访问互联网过程中,会遇到各种各样不容忽视的风险,例如员工主动故意的数据泄漏,后台应用程序偷偷向外部发信息,木马间谍软件的外联,以及各种挖…...
优维低代码实践:菜单
优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。 优维…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...
