[C++]红黑树

一、概念

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

性质:

  • 每个结点不是红色就是黑色
  • 根节点是黑色的
  • 如果一个节点是红色的,则它的两个孩子结点是黑色的
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  • 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

二、红黑树的结点定义

enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(T data = T()):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_col(RED)
};

二、红黑树的插入

1、插入结点

		Node* root = GetRoot();if (root == nullptr){root = new Node(data);root->_parent = _head;_head->_parent = root;return true;}Node* cur = root;Node* parent = root;//找到data节点插入的位置while (cur){if (data < cur->_data){parent = cur;cur = cur->_left;}else if (data > cur->_data){parent = cur;cur = cur->_right;}}//插入data节点cur = new Node(data);if (data < parent->_data){cur->_parent = parent;parent->_left = cur;}else{cur->_parent = parent;parent->_right = cur;}

若插入结点的父亲为黑色,则结束。

若插入结点的父亲为红色,则出现了连续的红色结点,违反规则,需要处理。 

2、调整

如插入结点后,该树违反了红黑树的规则,则对其进行调整。

(1)cur红,parent红,gparent黑,uncle存在且红

可以看出,该结点插入后,违反了规则: 如果一个节点是红色的,则它的两个孩子结点是黑色的。

此时,需要对其进行变色:将parent、uncle改为黑,gparent改为红,然后将gparent赋值给cur,若此时cur的父结点为红,则继续向上调整。

(2)cur红,parent红,gparent黑,uncle不存在

 此时cur一定是新插入的结点。否则不符合红黑树的规则:不能出现连在一起的红色结点。调整后:

 

(3)cur红,parent红,gparent黑,uncle存在且为黑

(1)基础情况 

此时cur一定不是新增结点,cur一定是由黑色变为红色的,否则就违反了红黑树的规则:每条路径黑色结点相同。

cur变红前的一种可能情况:

注:c:包含一个黑色结点的红黑树 (4种)

d/e:null或一个红色结点(4种)

调整后(旋转+变色):

我们将parent变为黑色,而非将cur变为黑色(即parent为红,cur和gparent为黑),是因为如果parent为红,那么当parent不是根结点的时候,仍需向上处理,即parent为红非终结态。

(2)单旋后转变为基础情况

对于这种情况,我们需要做以下处理:

若parent为gparent的左孩子,cur为parent的右孩子,则针对parent做左单旋转;
若parent为gparent的右孩子,cur为parent的左孩子,则针对parent做右单旋转。

旋转完成后,即转变为基础情况:

3、插入完整代码

	bool Insert(const T& data){Node* root = GetRoot();if (root == nullptr){root = new Node(data);root->_parent = _head;_head->_left = root;_head->_right = root;_head->_parent = root;root->_col = BLACK;return true;}Node* cur = root;Node* parent = root;//找到data节点插入的位置while (cur){if (data < cur->_data){parent = cur;cur = cur->_left;}else if (data > cur->_data){parent = cur;cur = cur->_right;}else{return false;}}//插入data节点cur = new Node(data);if (data < parent->_data){cur->_parent = parent;parent->_left = cur;}else{cur->_parent = parent;parent->_right = cur;}//更新_head结点链接关系T min = _head->_left->_data;T max = _head->_right->_data;if (data < min){_head->_left = cur;}if (data > max){_head->_right = cur;}//调整while (parent && parent->_col == RED){Node* gparent = parent->_parent;if (parent == gparent->_left){Node* uncle = gparent->_right;//cur红,parent红,gparent黑,uncle存在且红if (uncle && uncle->_col == RED){//调整颜色parent->_col = uncle->_col = BLACK;gparent->_col = RED;//继续向上处理cur = gparent;parent = cur->_parent;}else if(uncle == nullptr){//cur红,parent红,gparent黑,uncle不存在if (cur == parent->_left){//右旋RotateR(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{RotateL(parent);RotateR(gparent);//变色cur->_col = BLACK;parent->_col = gparent->_col = RED;}}else if (uncle && uncle->_col == BLACK){//cur红,parent红,gparent黑,uncle存在且为黑if (cur == parent->_left){RotateR(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{//对parent左单旋后转变为上面的情况RotateL(parent);RotateR(gparent);//变色cur->_col = BLACK;gparent->_col = RED;}}}else if(parent = gparent->_right){Node* uncle = gparent->_left;//cur红,parent红,gparent黑,uncle存在且红if (uncle && uncle->_col == RED){//调整颜色parent->_col = uncle->_col = BLACK;gparent->_col = RED;//继续向上处理cur = gparent;parent = cur->_parent;}else if (uncle == nullptr){//cur红,parent红,gparent黑,uncle不存在if (cur == parent->_right){//左旋RotateL(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{RotateR(parent);RotateL(gparent);//变色cur->_col = BLACK;parent->_col = gparent->_col = RED;}}else if (uncle && uncle->_col == BLACK){//cur红,parent红,gparent黑,uncle存在且为黑if (cur == parent->_right){RotateL(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{//对parent右单旋后转变为上面的情况RotateR(parent);RotateL(gparent);//变色cur->_col = BLACK;gparent->_col = RED;}}}}_head->_parent->_col = BLACK;return true;}

三、验证是否为红黑树

验证是否为红黑树,则需要根据红黑树的规则来确定:

没有两个连续的红色结点;

每条路径包含相同的黑色结点。

	bool IsValidRBTRee(){Node* root = GetRoot();if (root->_col == RED){return false;}if (root == nullptr){return true;}int blackc = GetBlackCount(GetRoot());int blacknum = 0;_IsValidRBTRee(GetRoot(), blackc, blacknum);}bool _IsValidRBTRee(Node* root, size_t blackCount, size_t pathBlack){if (root == nullptr){pathBlack++;if (pathBlack != blackCount){cout << "各路径黑色结点数量不同" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){return false;}if (root->_col == BLACK){pathBlack++;}return _IsValidRBTRee(root->_left, blackCount, pathBlack)&& _IsValidRBTRee(root->_right, blackCount, pathBlack);}

四、完整代码

enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(T data = T()):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_col(RED){}
};// 请模拟实现红黑树的插入--注意:为了后序封装map和set,本文在实现时给红黑树多增加了一个头结点
template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:RBTree(){_head = new Node;_head->_left = _head;_head->_right = _head;_head->_parent = _head;}// 在红黑树中插入值为data的节点,插入成功返回true,否则返回false// 注意:为了简单起见,本次实现红黑树不存储重复性元素bool Insert(const T& data){Node* root = GetRoot();if (root == nullptr){root = new Node(data);root->_parent = _head;_head->_left = root;_head->_right = root;_head->_parent = root;root->_col = BLACK;return true;}Node* cur = root;Node* parent = root;//找到data节点插入的位置while (cur){if (data < cur->_data){parent = cur;cur = cur->_left;}else if (data > cur->_data){parent = cur;cur = cur->_right;}else{return false;}}//插入data节点cur = new Node(data);if (data < parent->_data){cur->_parent = parent;parent->_left = cur;}else{cur->_parent = parent;parent->_right = cur;}//更新_head结点链接关系T min = _head->_left->_data;T max = _head->_right->_data;if (data < min){_head->_left = cur;}if (data > max){_head->_right = cur;}//调整while (parent && parent->_col == RED){Node* gparent = parent->_parent;if (parent == gparent->_left){Node* uncle = gparent->_right;//cur红,parent红,gparent黑,uncle存在且红if (uncle && uncle->_col == RED){//调整颜色parent->_col = uncle->_col = BLACK;gparent->_col = RED;//继续向上处理cur = gparent;parent = cur->_parent;}else if(uncle == nullptr){//cur红,parent红,gparent黑,uncle不存在if (cur == parent->_left){//右旋RotateR(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{RotateL(parent);RotateR(gparent);//变色cur->_col = BLACK;parent->_col = gparent->_col = RED;}}else if (uncle && uncle->_col == BLACK){//cur红,parent红,gparent黑,uncle存在且为黑if (cur == parent->_left){RotateR(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{//对parent左单旋后转变为上面的情况RotateL(parent);RotateR(gparent);//变色cur->_col = BLACK;gparent->_col = RED;}}}else if(parent = gparent->_right){Node* uncle = gparent->_left;//cur红,parent红,gparent黑,uncle存在且红if (uncle && uncle->_col == RED){//调整颜色parent->_col = uncle->_col = BLACK;gparent->_col = RED;//继续向上处理cur = gparent;parent = cur->_parent;}else if (uncle == nullptr){//cur红,parent红,gparent黑,uncle不存在if (cur == parent->_right){//左旋RotateL(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{RotateR(parent);RotateL(gparent);//变色cur->_col = BLACK;parent->_col = gparent->_col = RED;}}else if (uncle && uncle->_col == BLACK){//cur红,parent红,gparent黑,uncle存在且为黑if (cur == parent->_right){RotateL(gparent);//变色parent->_col = BLACK;gparent->_col = RED;}else{//对parent右单旋后转变为上面的情况RotateR(parent);RotateL(gparent);//变色cur->_col = BLACK;gparent->_col = RED;}}}}_head->_parent->_col = BLACK;return true;}// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptrNode* Find(const T& data){Node* cur = GetRoot();Node* parent = cur;while (cur){if (data > cur->_data){parent = cur;cur = cur->_right;}else if (data < cur->_data){parent = cur;cur = cur->_left;}else{return cur;}}return nullptr;}// 获取红黑树最左侧节点Node* LeftMost(){return _head->_left;}// 获取红黑树最右侧节点Node* RightMost(){return _head->_right;}// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测bool IsValidRBTRee(){Node* root = GetRoot();if (root->_col == RED){return false;}if (root == nullptr){return true;}int blackc = GetBlackCount(GetRoot());int blacknum = 0;_IsValidRBTRee(GetRoot(), blackc, blacknum);}void InOrder(){_InOrder(GetRoot());}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}size_t GetBlackCount(Node* root){size_t ret = 0;Node* cur = root;while (cur){if (cur->_col == BLACK){ret++;}cur = cur->_left;}ret++;return ret;}bool _IsValidRBTRee(Node* root, size_t blackCount, size_t pathBlack){if (root == nullptr){pathBlack++;if (pathBlack != blackCount){cout << "各路径黑色结点数量不同" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){return false;}if (root->_col == BLACK){pathBlack++;}return _IsValidRBTRee(root->_left, blackCount, pathBlack)&& _IsValidRBTRee(root->_right, blackCount, pathBlack);}// 右单旋void RotateR(Node* parent){Node* pprent = parent->_parent;Node* pl = parent->_left;Node* PRL = pl->_right;Node* PR = parent;//更新与上一节点的链接关系if (parent == GetRoot()){_head->_parent = pl;pl->_parent = nullptr;}else{if (pprent->_left == parent){pprent->_left = pl;}else{pprent->_right = pl;}pl->_parent = pprent;}pl->_right = PR;PR->_parent = pl;PR->_left = PRL;if (PRL)PRL->_parent = PR;}// 左单旋void RotateL(Node* parent){Node* pprent = parent->_parent;Node* pr = parent->_right;Node* PLR = pr->_left;Node* PL = parent;//更新与上一节点的链接关系if (parent == GetRoot()){_head->_parent = pr;pr->_parent = nullptr;}else{if (pprent->_left == parent){pprent->_left = pr;}else{pprent->_right = pr;}pr->_parent = pprent;}pr->_left = PL;PL->_parent = pr;PL->_right = PLR;if (PLR)PLR->_parent = PL;}// 为了操作树简单起见:获取根节点Node* GetRoot(){if (_head->_parent == _head){return nullptr;}else{return _head->_parent;}}
private:Node* _head;
};

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

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

相关文章

CIM分级

定义 以建筑信息模型&#xff08;BIM&#xff09;、地理信息系统&#xff08;GIS&#xff09;、物联网&#xff08;IoT&#xff09;等技术为基础&#xff0c;整合城市地上地下、室内室外、历史现状未来多维多尺度信息模型数据和城市感知数据&#xff0c;构建起三维数字空间的城…

面试问到Spring中的@Autowired注解,可以这样答

前言 在Spring框架中&#xff0c;依赖注入是一个核心概念&#xff0c;它允许将一个对象的依赖关系外部化并由Spring容器来管理。Autowired注解是实现这一点的关键工具之一。当然&#xff0c;这块知识也是面试官们老生常谈的问题。 下面就跟着博主的步伐&#xff0c;一起来探讨…

GCANet去雾算法

目录 1. 引言 2. 门控上下文注意机制&#xff08;GCA&#xff09; 3. 去雾流程 4. 模型代码 5. GCANet的优势 6. 去雾效果 1. 引言 GCANet(Gate-Controlled Attention Network)是一种用于图像去雾的深度学习算法&#xff0c;通过引入注意力机制来改进传统的去雾方法&…

大模型场景应用汇总(持续更新)

一、应用场景 1.办公场景 智能办公&#xff1a;文案生成&#xff08;协助构建大纲优化表达内容生成&#xff09;、PPT美化&#xff08;自动排版演讲备注生成PPT&#xff09;、数据分析&#xff08;生成公式数据处理表格生成&#xff09;。 智能会议&#xff1a;会议策划&…

密码学基础概念

加密性 什么是加密&#xff1f; 1.对原有的明文数据&#xff0c;执行某种运算&#xff0c;得到密文数据。 2.密文数据对于未授权人员而言&#xff0c;在一定上程度上加大了解读的难度 3.加密功能用于实现机密性 什么是密钥&#xff1f; 1.如同持有保险柜钥匙才能打开保险柜…

排序进阶----插入排序,希尔排序

各位看官们好&#xff0c;接下来鄙人想与大家分享的实现被称为六大排序之一的插入排序。其实关于这六大排序在我们最开始就已经接触过了。我们在最开始学习c语言的时候&#xff0c;我们要学习到其中之一的冒泡排序。虽然现在看起来冒泡排序确实是没有太大的实际效果&#xff0c…

618必买的数码好物有哪些?盘点兼具设计与实用的数码好物分享

随着618购物节的到来&#xff0c;数码爱好者们又开始跃跃欲试&#xff0c;期待在这个年度大促中寻找到自己心仪的数码好物&#xff0c;在这个数字化时代&#xff0c;数码产品不仅是我们日常生活的必需品&#xff0c;更是提升生活品质的重要工具&#xff0c;那么在众多的数码产品…

HNU-计算机体系结构-实验1-RISC-V流水线

计算机体系结构 实验1 计科210X 甘晴void 202108010XXX 1 实验目的 参考提供为了更好的理解RISC-V&#xff0c;通过学习RV32I Core的设计图&#xff0c;理解每条指令的数据流和控制信号&#xff0c;为之后指令流水线及乱序发射实验打下基础。 参考资料&#xff1a; RISC-…

Ubuntu20.04升级到22.04之后出现的问题

项目场景&#xff1a; 之前一致使用的是Ubuntu20.04&#xff0c;虽然丑了点&#xff0c;但是用着没什么问题&#xff0c;最近没能按捺住好奇心&#xff0c;升级到了22.04&#xff0c;升级后颜值有所提高&#xff0c;但是也带来了一些问题。 从20.04升级到22.04&#xff0c;起始…

sendmail发送邮件配置详解?如何正确设置?

sendmail发送邮件如何保障安全&#xff1f;AokSend有何安全措施&#xff1f; 为了确保sendmail发送邮件的高效性和安全性&#xff0c;正确配置是至关重要的。本文将详细介绍sendmail发送邮件的配置步骤&#xff0c;并探讨如何保障sendmail发送邮件的安全性。同时&#xff0c;我…

界面控件DevExpress WinForms的流程图组件 - 可完美复制Visio功能(一)

DevExpress WinForms的Diagram&#xff08;流程图&#xff09;组件允许您复制Microsoft Visio中的许多功能&#xff0c;并能在下一个Windows Forms项目中引入信息丰富的图表、流程图和组织图。 P.S&#xff1a;DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows F…

2024年中国金融行业网络安全研究报告

网络安全一直是国家安全的核心组成部分&#xff0c;特别是在金融行业&#xff0c;金融机构拥有大量的敏感数据&#xff0c;包括个人信息、交易记录、财务报告等&#xff0c;这些数据的安全直接关系到消费者的利益和金融市场的稳定&#xff0c;因此金融行业在网络安全建设领域一…

成都市酷客焕学新媒体科技有限公司:助力品牌打破困境!

在数字化浪潮的推动下&#xff0c;营销策略对品牌的发展愈发关键。成都市酷客焕学新媒体科技有限公司&#xff0c;作为短视频营销领域的佼佼者&#xff0c;凭借其卓越的策略和实力&#xff0c;助力众多品牌在信息海洋中脱颖而出&#xff0c;实现品牌的显著增长。 酷客焕学专注于…

部署八戒-Chat-1.8B 模型

1 简单介绍 八戒-Chat-1.8B 八戒-Chat-1.8B是运用 InternLM2-Chat-1.8B 模型进行微调训练的优秀成果。其中&#xff0c;八戒-Chat-1.8B 是利用《西游记》剧本中所有关于猪八戒的台词和语句以及 LLM API 生成的相关数据结果&#xff0c;进行全量微调得到的猪八戒聊天模型。作为 …

python-使用API

python-使用API 使用github的api-即url地址请求数据 https://api.github.com/search/repositories?qlanguage:python&sortstars #这个调用返回GitHub当前托管了多少个Python项目&#xff0c;还有有关最受欢迎的Python仓库的信息。在浏览器中输入上面地址可以看到该接口&…

论文《Sensor and Sensor Fusion Technology in Autonomous Vehicles: A Review》详细解析

论文《Sensor and Sensor Fusion Technology in Autonomous Vehicles: A Review》详细解析 摘要 该论文对自动驾驶汽车中的传感器和传感器融合技术进行了全面回顾。它评估了各种传感器&#xff08;如相机、LiDAR、雷达&#xff09;的能力和技术性能&#xff0c;并讨论了多传感…

fastadmin部署后JSHint报错,导致Config::getValueByName()无法获取到值

问题 解决方案 一、本地 在phpstorm中&#xff0c;依次点击【设置】-【JSHint】-【取消勾选Enable】-【应用】即可。

无需安装的在线PS:打开即用

为什么想用在线PS网页版&#xff1f;Photoshop常用于平面设计&#xff0c;是不少设计师接触过的第一款设计软件。作为一款平面设计工具&#xff0c;ps功能太多&#xff0c;并且没有在线版&#xff0c;这不仅需要设计师花费时间学习软件&#xff0c;还需要设计师具备一定的设计能…

正邦科技(day2)

自动校准 问题&#xff1a;电量不准都可以直接去校准 校准方式&#xff1a;可程式变频电压 问题分析&#xff1a;他是通过软件去自动自动校准的&#xff0c;flash 清空的时候有缓存没有清空&#xff0c;或者互感器没有读取到问题 互感器&#xff1a;电流互感器的作用包括电流测…

【DrissionPage爬虫库 1】两种模式分别爬取Gitee开源项目

文章目录 DrissionPage爬虫库简介1. 浏览器操控模式&#xff08;类似于游戏中的后台模拟鼠标键盘&#xff09;2. 数据包收发模式&#xff08;类似于游戏中的协议封包&#xff09; 实战中学习需求&#xff1a;爬取Gitee开源项目的标题与描述解决方案1&#xff1a;用数据包方式获…