当前位置: 首页 > news >正文

【C++】红黑树的模拟实现

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘
🛸C++专栏C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

一、红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

在这里插入图片描述

二、红黑树的性质

  1. 每个结点不是红色就是黑色

  2. 根节点是黑色的

  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

    最优情况:全黑或每条路径都是一黑一红的满二叉树,高度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++】红黑树的模拟实现

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…...

【多线程】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&#xff09;查看网卡在网络层的配置信息 2&#xff09;查看网卡在数据链路层的配置信息 3&#xff09;添加或者删除临时的网卡 4&#xff09;禁用和启动指定网卡 2、修改配置文件 3、nmcli命令行…...

refresh rate

1920 x 1080 显卡刷新率 60...

使用 NGINX Unit 实施应用隔离

原文作者&#xff1a;Artem Konev - Senior Technical Writer 原文链接&#xff1a;使用 NGINX Unit 实施应用隔离 转载来源&#xff1a;NGINX 中文官网 NGINX 唯一中文官方社区 &#xff0c;尽在 nginx.org.cn NGINX Unit 特性集的最新动态之一是支持应用隔离&#xff0c;该特…...

2023/09/12 qtc++

实现一个图形类(Shape) &#xff0c;包含受保护成员属性:周长、面积&#xff0c; 公共成员函数:特殊成员函数书写 定义一个圆形类(Circle) &#xff0c;继承自图形类&#xff0c;包含私有属性:半径 公共成员函数:特殊成员函数…...

全科医学科常用评估量表汇总,建议收藏!

根据全科医学科医生的量表使用情况&#xff0c;笔者整理了10个常用的全科医学科量表&#xff0c;可在线评测直接出结果&#xff0c;可转发使用&#xff0c;可生成二维码使用&#xff0c;可创建项目进行数据管理&#xff0c;有需要的小伙伴赶紧收藏&#xff01; 日常生活能力量表…...

了解消息中间件的基础知识

为什么要使用消息中间件&#xff1f; 解耦&#xff1a;消息中间件可以使不同的应用程序通过解耦的方式进行通信&#xff0c;减少系统间的依赖关系提供异步通信&#xff1a;消息中间件可以实现异步消息传递&#xff0c;提高系统的响应性能。流量削峰&#xff1a;消息中间件可以…...

【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 &#xff0c;返回其节点值的 锯齿形层序遍历 。&#xff08;即先从左往右&#xff0c;再从右往左进行下一层遍历&#xff0c;以此类推&#xff0c;层与层之间交替进行&#xff09;。 示例 1&#xff1a; 输入&#xff1a;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 中的代码可以分为以下几种类型&#xff1a; 1.模板代码 模板代码是 Vue 中用来生成 HTML 的一种语法&#xff0c;可以通过 Vue 的模板语法和指令来动态渲染页面。模板代码一般写在 Vue 组件的 template 标签中。 2.JavaScript 代码 JavaScript 代码是 Vue 组件中用来…...

es6中includes用法

js中的includes用法 1.数组 includes 可以判断一个数组中是否包含某一个元素&#xff0c;并返回true 或者false [a,b,c].includes(a) true [a,b,c].includes(1) false includes可以包含两个参数&#xff0c;第二个参数表示判断的起始位置 起始位置第一个数字是0。 2.字符串 …...

QT中QRadioButton实现分组C++

通过对QRadioButton组件进行分组可解决QRadioButton组件的互斥性 实现如下。 假设已设计好UI并且有UI代码情况&#xff1a; 头文件引用&#xff1a; #include <QButtonGroup> 分组功能 &#xff0c;cpp文件代码实现&#xff1a; Your_Project::Your_Project(QWidge…...

kafka实战报错解决问题

需求 在一个在线商城中&#xff0c;用户下单后需要进行订单的处理。为了提高订单处理的效率和可靠性&#xff0c;我们使用Kafka来实现订单消息的异步处理。当用户下单后&#xff0c;订单信息会被发送到Kafka的一个Topic中&#xff0c;然后订单处理系统会从该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包&#xff0c;在react keep alive…...

【android 蓝牙开发——蓝牙耳机】

【android 蓝牙开发——传统蓝牙】 【android 蓝牙开发——BLE&#xff08;低功耗&#xff09;蓝牙 2021-10-09更新】 总结一下蓝牙开发的基本使用以及蓝牙耳机的断开和链接。 所以需权限&#xff1a; <uses-permission android:name"android.permission.ACCESS_FIN…...

Golang goroutine 进程、线程、并发、并行

goroutine 看一个需求 需求&#xff1a;要求统计1-200000000000的数字中&#xff0c;哪些是素数? 分析思路&#xff1a; 1)传统的方法&#xff0c;就是使用一个循环&#xff0c;循环的判断各个数是不是素数&#xff08;一个任务就分配给一个cpu去做&#xff0c;这样很不划算…...

如何做到安全上网

随着信息化的发展&#xff0c;企业日常办公越来越依赖互联网&#xff0c;而访问互联网过程中&#xff0c;会遇到各种各样不容忽视的风险&#xff0c;例如员工主动故意的数据泄漏&#xff0c;后台应用程序偷偷向外部发信息&#xff0c;木马间谍软件的外联&#xff0c;以及各种挖…...

优维低代码实践:菜单

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...