[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,一经查实,立即删除!

相关文章

每日一读: 硬件网卡tx支持哪些功能特性offload(ixgbe驱动为例)

ixgbe驱动 rte_eth_dev_info_get -> ixgbe_dev_info_get -> ixgbe_get_tx_port_offloads uint64_t ixgbe_get_tx_port_offloads(struct rte_eth_dev *dev) {uint64_t tx_offload_capa;struct ixgbe_hw *hw IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);tx_…

CIM分级

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

c/c++:离开停车场

题目描述(题目链接&#xff09; 你被困在一个多层停车场。您的任务是仅使用楼梯离开停车场。出口总是在一楼的右下角。 创建一个采用矩阵的函数&#xff0c;其中&#xff1a; 0 表示免费停车位。 1 表示楼梯。 2 表示您的起始位置&#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;通过引入注意力机制来改进传统的去雾方法&…

C#实现纳秒级的计时器功能

常用的 Windows API 方法 GetTickCount() 返回系统启动后经过的毫秒数。另一方面&#xff0c;GetTickCount() 函数仅有 1ms 的分辨精度&#xff0c;精度也很不好。 我们要另外寻找一种方法来精确测量时间。 Win32 API 使用 QueryPerformanceCounter() 和 QueryPerformanceFre…

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

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

P10442 「MYOI-R3」字符串

「MYOI-R3」字符串 题目描述 给定字符串 s , t s,t s,t。 现在你要在 s , t s,t s,t 中删除一些字符并将它们重新排列使 s t st st。 问操作后的 ∣ s ∣ |s| ∣s∣&#xff08;即字符串 s s s 的长度&#xff09;最大是多少&#xff1f; 输入格式 第一行一个字符串…

深入探索 MongoDB:高级索引解析与优化策略

MongoDB 是一种非常流行的 NoSQL 数据库&#xff0c;它支持丰富的索引类型和功能&#xff0c;以提高数据查询的效率和性能。本文将详细介绍 MongoDB 的高级索引&#xff0c;包括基本语法、常用命令、示例、应用场景、注意事项和总结。 基本语法 在 MongoDB 中&#xff0c;可以…

密码学基础概念

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

数据结构算法题day05

数据结构算法题day05 题目算法思想代码 题目 从有序表中删除所有其值重复的元素&#xff0c;使表中所有元素的值均不同。算法思想 第一个元素&#xff08;不重复&#xff09;依次向后扫描&#xff0c;不重复就保留&#xff0c;重复&#xff08;不保留&#xff09;就删除所有重…

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

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

【Xilinx约束】create_generated_clock 约束语法介绍

在 Xilinx Vivado 环境中&#xff0c;使用 create_generated_clock 约束来定义由锁相环&#xff08;PLL&#xff09;或时钟管理模块&#xff08;如 MMCM 或 DCM&#xff09;生成的时钟。这个约束用于指定生成时钟的属性&#xff0c;例如时钟的源、相位和频率。 基本语法 以下是…

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

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

【MySQL】 1130 -Host ‘14.*.**.*‘ is not allowed to connect to this MySQL server

这个错误表明MySQL服务器拒绝了来自IP地址为14.*.**.*的主机的连接请求。这通常是由于MySQL用户的主机限制引起的。 要解决这个问题&#xff0c;你需要在MySQL中允许指定主机的连接。你可以按照以下步骤操作&#xff1a; 使用具有足够权限的用户&#xff08;如root用户&#x…

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;起始…

【学习笔记】Axios、Promise

TypeScript 1、Axios 1.1、概述 1.2、axios 的基本使用 1.3、axios 的请求方式及对应的 API 1.4、axios 请求的响应结果结构 1.5、axios 常用配置选项 1.6、axios.create() 1.7、拦截器 1.8、取消请求2、Promise 2.1、封装 fs 读…

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…