010 数据结构_红黑树

前言

本文将会向你介绍红黑树的概念、性质,以及如何手撕红黑树

1 文章重点

文本首先引入红黑树的概念和性质,性质非常重要对于后面的插入操作来说,文章的核心放在了插入部分,另外看插入部分之前记得看声名和节点的定义哦~

2 引入红黑树

2.1概念

首先红黑树是一颗二叉搜索树,每个节点都有颜色,红色或黑色,最长路径最多是最短路径的二倍

2.2性质

1、 每个节点不是红色就是黑色。
2、 根部节点是黑色的。
3、 一条路径上不能出现连续的红色节点
4、每条路径都必须包含相同数量的黑色节点。

3 测试

	// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptrNode* Find(const pair<K, V>& kv){Node* cur = _root;Node* parent = _root;Node* grandParent = parent->_parent;//判断是否为空树if (_root == nullptr){return nullptr;}//寻找插入位置else{Node* parent = cur;while (cur){//记录父节点的位置,便于后续的链接操作parent = cur;//向左遍历if (cur->_kv.first > kv.first){cur = cur->_left;}//向右遍历else if (cur->_kv.first < kv.first){cur = cur->_right;}//已有else return cur;}}}//中序遍历,验证是否为二叉搜索树void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}size_t _Height(Node* root){if (root == nullptr){return 0;}int leftHeight = _Height(root->_pLeft);int rightHeight = _Height(root->_pRight);return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1;}int Height(){return _Height(_root);}bool Check(Node* root, int blackNum, const int ref){if (root == nullptr)  //到根部看看当前路径黑色节点和标准值是否一致{//cout << blackNum << endl;if (blackNum != ref){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}// 检查子比较复杂,可以反过来去检查红节点父是否为黑色if (root->_cl == RED && root->_parent->_cl == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_cl == BLACK){++blackNum;  //为黑节点加一}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);}bool _IsBalance(Node* root){if (root == nullptr){return true;}if (root->_cl == RED){return false;}int ref = 0;Node* cur = root;//计算一条路径的黑色节点标准值while (cur){if (cur->_cl == BLACK){ref++;}cur = cur->_left;}int blackNum = 0;return Check(root, blackNum, ref); }bool IsBalance(){return _IsBalance(_root);}

Check 函数是一个递归函数,用于检查从当前节点到叶子节点的路径上的黑色节点个数是否与参考值 ref 相等。
参数 root 表示当前节点指针,blackNum 表示根节点到当前节点的路径上的黑色节点个数,ref 是参考值。
如果当前节点为空指针,即到达了叶子节点,那么检查路径上的黑色节点个数 blacknum 是否等于参考值 ref,如果不相等则返回false,否则返回 true。
如果当前节点的颜色为红色,并且父节点也是红色,表示存在连续的红节点,不符合红黑树的性质,返回 false。
如果当前节点的颜色为黑色,将 blackNum 值加一。 递归地调用 Check 函数检查左子节点和右子节点,并将当前节点的黑色节点个数blacknum 作为参数传递。

IsBalance 函数用于检查整个红黑树是否符合红黑树的性质。
如果红黑树为空树,即根节点为空,认为是一棵合法的红黑树,返回 true。
如果根节点的颜色是红色,违反了红黑树的性质,返回 false。
初始化 blacknum 为 0,用于记录每条路径的黑色节点个数。 初始化 ref 为 0,作为参考值。
通过遍历从根节点到最左子节点的路径,统计参考值 ref,即路径上的黑色节点个数。 调用 Check
函数,传入根节点、路径上的黑色节点个数 blacknum 和参考值 RefVal 进行检查

插入一千个数据(未全列出)
在这里插入图片描述

4 插入(重点)

声名

为了方便叙述,将进行以下定义
cur©——当前节点
parent§——父节点
grandParent(g)——父节点的父节点
uncle(u)——父节点的兄弟节点

节点的定义

enum Color
{RED,BLACK
};
template<class K, class V>
struct RBTreeNode
{RBTreeNode* _left;RBTreeNode* _right;RBTreeNode* _parent;RBTreeNode* _pHead;Color _cl;pair<K, V> _kv;
template<class K, class V>RBTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr),_pHead(nullptr), _cl(RED), _kv(kv){}
};

_left:指向左子节点的指针。
_right:指向右子节点的指针。
_parent:指向父节点的指针。
_kv:存储键值对的成员变量。这里使用了 pair<K, V> 类型来表示键值对,其中 K 是键的类型,V 是值的类型。
_cl:表示节点的颜色。这里使用枚举类型来表示,其中 RED 表示红色,BLACK 表示黑色,构造出来的节点默认为红色

插入

对于新插入节点,我们设置为红色,原因是红黑树每条路径都必须包含相同数量的黑色节点(性质4),新插入红节点不一定破坏红黑树的结构,新插入黑色节点一定不符合性质4而且很难调整(新增一条路径的黑色节点,其他路径也需要增加(会很麻烦))

bool Insert(const pair<K, V>& kv){				//记录当前节点Node* cur = _root;//记录父节点Node* parent = _root;//判断是否为空树if (_root == nullptr){//直接插入_root = new Node(kv);//插入成功_root->_cl = BLACK;return true;}//寻找插入位置else{	while (cur){//记录父节点的位置,便于后续的链接操作parent = cur;//向左遍历if (cur->_kv.first > kv.first){cur = cur->_left;}//向右遍历else if (cur->_kv.first < kv.first){cur = cur->_right;}//已有else return false;}cur = new Node(kv);//插入+链接if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}//链接cur->_parent = parent;}//父亲为红,变色调整while (parent && parent->_cl == RED){//以下代码暂时省略......//1、parent为grandparent的左//情况一:uncle存在且为红,变色即可//无需讨论cur的位置,变色即可//情况二:uncle不存在或者为黑,旋转+变色//需讨论cur的位置//(1)cur为parent的左(进行右单旋)//	 g// p   u//c//(2)cur为parent的右(进行左右双旋)//	 g// p   u//	c//2、parent为grandparent的右//情况一:uncle存在且为红,变色即可//无需讨论cur的位置,变色即可//情况二:uncle不存在或者为黑,旋转+变色//(1)cur为parent的右(进行左单旋)//	 g// u   p//		c//(2)cur为parent的右(进行右左双旋)//	 g// u   p//	 c}	//保持根部为黑色_root->_cl = BLACK;return true;}

注意:所有的情况解决方案都是根据规律总结出来的,由于将所有的情况列出来过于复杂,所以用抽象的子树来代替(三角形代表的就是抽象的子树),在这一情况中将会告诉你如果不用抽象子树来代替会有多复杂

(1)情况一:parent为grandparent的左

1.1uncle存在且为红,变色即可

Node* uncle = grandParent->_right;
if (uncle && uncle->_cl == RED)
{parent->_cl = uncle->_cl = BLACK;grandParent->_cl = RED;//继续向上调整cur = grandParent;parent = cur->_parent;
}

在这里插入图片描述
三角形表示抽象子树,下面会举例说明这里为什么会用抽象子树来表示
在这里插入图片描述
当c,d,e是没有黑色节点的子树,此时cur是新增,此时c,d,e子树处为一个红节点或者为空,a,b为空。我们只需要通过新插入节点以及p,u、g节点进行变色即可,如图将p,u变黑,将g变红即可
在这里插入图片描述
当c,d,e子树是包含一个黑色节点的子树,如图此时c(cur)便不是新插入的节点了,最下边的红色节点才是
在这里插入图片描述

这是c,d,e子树可能的情况,然而插入位置有4个,cde形状组合:444,合计组合为64*4 = 256种情况,如果c,d,e子树是包含2个黑色节点及以上的,那么情况是分析不完的
在这里插入图片描述
其实你会发现,只要满足某个规律(如下)
在这里插入图片描述
然后做出应有的调整
在这里插入图片描述
最后在向上调整就好了
在这里插入图片描述
以下情况(非必须)都会用抽象子树来模拟规律

1.2uncle不存在或者为黑,旋转+变色

1.2.1cur为parent的左(进行右单旋)
//(1)cur为parent的左
//	 g
// p   u
//c
if (cur == parent->_left)
{RotateR(grandParent);grandParent->_cl = RED;parent->_cl = BLACK;
}

为了能让你更好的理解。此情况(uncle不存在或者为黑)的调整将会结合uncle存在且为红,变色即可的情况
如图:c、d、e是包含一个黑色节点的子树
在这里插入图片描述

规律:uncle存在且为红,变色即可并向上调整
在这里插入图片描述
我们将会发现此时满足u为黑
在这里插入图片描述

右旋
在这里插入图片描述
在这里插入图片描述
变色
在这里插入图片描述
注意c、d、e是包含一个黑色节点的子树
此时你便可以看看是否满足红黑树的性质了
在这里插入图片描述

1.2.2cur为parent的右(进行右左双旋)
//(1)cur为parent的右
//	 g
// p   u
//	c
else
{//左右双旋RotateL(parent);RotateR(grandParent);grandParent->_cl = RED;cur->_cl = BLACK;
}

在这里插入图片描述

(2)情况二:parent为grandparent的右

2.1uncle存在且为红,变色即可

Node* uncle = grandParent->_left;
if (uncle && uncle->_cl == RED)
{parent->_cl = uncle->_cl = BLACK;grandParent->_cl = RED;//继续向上调整cur = grandParent;parent = cur->_parent;
}

在这里插入图片描述

2.2uncle不存在或者为黑,旋转+变色

2.2.1cur为parent的右(进行左单旋)
//(1)cur为parent的右(左单旋)
//	 g
// u   p
//		c
if (cur == parent->_right)
{RotateL(grandParent);grandParent->_cl = RED;parent->_cl = BLACK;
}

在这里插入图片描述

2.2.2cur为parent的左(进行右左双旋)
//(1)cur为parent的右(右左双旋)
//	 g
// u   p
//	 c
RotateR(parent);
RotateL(grandParent);
grandParent->_cl = RED;
cur->_cl = BLACK;

在这里插入图片描述

5 全部代码

enum Color
{RED,BLACK
};
template<class K, class V>
struct RBTreeNode
{RBTreeNode* _left;RBTreeNode* _right;RBTreeNode* _parent;RBTreeNode* _pHead;Color _cl;pair<K, V> _kv;
template<class K, class V>RBTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr),_pHead(nullptr), _cl(RED), _kv(kv){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://左单旋void RotateL(Node* pParent){Node* subR = pParent->_right;Node* subRL = subR->_left;	//可能为空pParent->_right = subRL;subR->_left = pParent;Node* pPnode = pParent->_parent; //原来父亲的父亲pParent->_parent = subR;//可能为空if (subRL){subRL->_parent = pParent;}//链接:旋转整棵树if (_root == pParent){_root = subR;subR->_parent = nullptr;}//链接:旋转子树else{if (pPnode->_left == pParent){pPnode->_left = subR;}else if (pPnode->_right == pParent){pPnode->_right = subR;}subR->_parent = pPnode;}}// 右单旋void RotateR(Node* pParent){Node* subL = pParent->_left;Node* subLR = subL->_right;pParent->_left = subLR;//可能为空if (subLR){subLR->_parent = pParent;}Node* pPnode = pParent->_parent;subL->_right = pParent;pParent->_parent = subL;//旋转部分子树if (_root == pParent){			_root = subL;subL->_parent = nullptr;}//旋转整棵子树else{if (pPnode->_left == pParent){pPnode->_left = subL;}else{pPnode->_right = subL;}subL->_parent = pPnode;}}bool Insert(const pair<K, V>& kv){				//记录当前节点Node* cur = _root;//记录父节点Node* parent = _root;//判断是否为空树if (_root == nullptr){//直接插入_root = new Node(kv);//插入成功_root->_cl = BLACK;return true;}//寻找插入位置else{	while (cur){//记录父节点的位置,便于后续的链接操作parent = cur;//向左遍历if (cur->_kv.first > kv.first){cur = cur->_left;}//向右遍历else if (cur->_kv.first < kv.first){cur = cur->_right;}//已有else return false;}cur = new Node(kv);//插入+链接if (parent->_kv.first > kv.first){parent->_left = cur;}else{parent->_right = cur;}//链接cur->_parent = parent;}//父亲为红,变色调整while (parent && parent->_cl == RED){//记录原来节点的父亲的父节点Node* grandParent = parent->_parent;//情况一:uncle存在且为红,变色即可if (parent == grandParent->_left){Node* uncle = grandParent->_right;if (uncle && uncle->_cl == RED){parent->_cl = uncle->_cl = BLACK;grandParent->_cl = RED;//继续向上调整cur = grandParent;parent = cur->_parent;}//情况二:uncle不存在或者为黑,旋转+变色else{//(1)cur为parent的左//	 g// p   u//cif (cur == parent->_left){RotateR(grandParent);grandParent->_cl = RED;parent->_cl = BLACK;}//(1)cur为parent的右//	 g// p   u//	celse{//左右双旋RotateL(parent);RotateR(grandParent);grandParent->_cl = RED;cur->_cl = BLACK;}break;}}//parent为grandparent的右else{//情况一:uncle存在且为红,变色即可Node* uncle = grandParent->_left;if (uncle && uncle->_cl == RED){parent->_cl = uncle->_cl = BLACK;grandParent->_cl = RED;//继续向上调整cur = grandParent;parent = cur->_parent;}//情况二:uncle不存在或者为黑,旋转+变色else{//(1)cur为parent的右(左单旋)//	 g// u   p//		cif (cur == parent->_right){RotateL(grandParent);grandParent->_cl = RED;parent->_cl = BLACK;}//(1)cur为parent的右(左单旋)else{//(1)cur为parent的右(右左双旋)//	 g// u   p//	 cRotateR(parent);RotateL(grandParent);grandParent->_cl = RED;cur->_cl = BLACK;}break;}}}	//保持根部为黑色_root->_cl = BLACK;return true;}

在这里插入图片描述

小结

今日的分享就到这里啦,如果本文存在疏漏或错误的的地方,还请您能够指出

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

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

相关文章

嵌入式门槛高吗?

今日话题&#xff0c;嵌入式门槛高吗&#xff1f;在嵌入式领域&#xff0c;门槛因公司和职位的不同而异。普通的嵌入式岗位门槛相对较低&#xff0c;通常要求掌握一些C语言编程和单片机相关知识&#xff0c;可以制作简单的电子产品&#xff0c;但相应的工资较低。然而&#xff…

解决Flutter运行报错Could not run build/ios/iphoneos/Runner.app

错误场景 更新了IOS的系统版本为最新的17.0, 运行报以下错误 Launching lib/main.dart on iPhone in debug mode... Automatically signing iOS for device deployment using specified development team in Xcode project: GN3DCAF71C Running Xcode build... Xcode build d…

PG 常用维护性 SQL

文章目录 查看哪些角色对表有增删改查权限查看哪些角色对函数有执行权限根据序列名获取表及列信息查看postgresql数据库用户系统权限、对象权限查看所有主键及其相关字段信息查看 排除主键索引之外的 其他所有唯一性约束与唯一索引给 data 用户授予 create publication 权限统计…

典型的ETL使用场景

典型的ETL使用场景 ETL( Extract&#xff0c;Transform&#xff0c;Load)是一种用于数据集成和数据转换的常用技术。它主要用于从多个数据源中提取数据&#xff0c;对数据进行清洗、转换和整合&#xff0c;最后加载到目标系统中。ETL 的使用场景非常广泛&#xff0c;下面将介绍…

力扣labuladong一刷day28天二叉树

力扣labuladong一刷day28天二叉树 文章目录 力扣labuladong一刷day28天二叉树一、104. 二叉树的最大深度二、144. 二叉树的前序遍历三、543. 二叉树的直径 一、104. 二叉树的最大深度 题目链接&#xff1a;https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 思路&a…

R语言中如何改变表格数据的填充顺序

#搬运过来的&#xff0c;看不懂请看原出处 原作者&#xff1a; 熊荣川 六盘水师范学院生物信息学实验室 xiongrongchuan126.com http://blog.sciencenet.cn/u/Bearjazz 通常在R语言中矩阵&#xff08;表格&#xff09;数据的填充默认顺序为先列后行&#xff0c;从左到右。…

Theamleaf导出pdf模版编写(原始th/td编写表格)

需求&#xff1a;简单的theamleaf编写表格就是简单的th/td&#xff0c;新需求是导出的模版是学员table表&#xff0c;每个项目的学员数量是不定的&#xff0c;所以用到 <tr th:each"item,start:${studentList}"> 所有代码&#xff1a; <!DOCTYPE html>…

python 实现 AIGC 大模型中的概率论:生日问题的基本推导

在上一节中&#xff0c;我们对生日问题进行了严谨的阐述&#xff1a;假设屋子里面每个人的生日相互独立&#xff0c;而且等可能的出现在一年 365 天中的任何一天&#xff0c;试问我们需要多少人才能让某两个人的生日在同一天的概率超过 50%。 处理抽象逻辑问题的一个入手点就是…

【华为OD题库-057】MELON的难题-java

题目 MELON有一堆精美的雨花石(数量为n&#xff0c;重量各异)&#xff0c;准备送给S和W。MELON希望送给俩人的雨花石重星一致&#xff0c;请你设计一个程序帮MELON确认是否能将雨花石平均分配。 输入描述 第1行输入为雨花石个数:n&#xff0c;0<n <31. 第2行输入为空格分…

单片机实现数码管动态显示

动态显示的特点是将所有位数码管的段选线并联在一起&#xff0c;由位选线控制是哪一位数码管有效。这样一来&#xff0c;就没有必要每一位数码管配一个锁存器&#xff0c;从而大大地简化了硬件电路。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码…

vmware_ubuntu_双向拷贝问题

vmware 中 ubuntu 系统与 Host 机无法双向拷贝 在 vmware workstation 中最小化安装好 ubuntu 20.04 后&#xff0c;开机后发现无法将 Host 机中拷贝的内容粘贴到 ubuntu 中。 实践搜索到的方案&#xff1a;https://blog.csdn.net/luobeihai/article/details/123885756&#…

【bash指令全集合】最全教程-持续更新!

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于新西兰奥克兰大学攻读IT硕士学位。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。跨领域…

Centos7.4安装nginx1.24.0_安装详细步骤---Linux工作笔记066

以前安装的太模糊了,干脆重新写一个: 1.首先下载对应的nginx-1.24.0.tar.gz安装文件 2.然后: 去执行命令 安装依赖 yum install -y gcc yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum install -y openssl openssl-devel 3.然后:去解压 tar -zxvf ngi…

一文讲透Python机器学习决策树算法的基本概念与原理

1.决策树算法的基本特点与优势 决策树算法是一种有监督、非参数、简单、高效的机器学习算法。相对于非监督式学习方法&#xff0c;决策树算法由于充分利用了响应变量的信息&#xff0c;因此能够很好地克服噪声问题&#xff0c;在分类及预测方面效果更佳。决策树的决策边界为矩…

【MySQL】MySQL数据库基础

MySQL数据库基础 一、为什么要有数据库&#xff1f;二、 数据库软件的构成数据库服务器&#xff0c;数据库&#xff0c;表关系主流数据库 三、基本使用1、连接服务器2、服务器管理3、MySQL配置文件4、数据库的简单操作5、数据逻辑存储 四、MySQL架构SQL分类MySQL客户端存储引擎…

smartkit巡检E9000设备

https://support.huawei.com/enterprise/zh/doc/EDOC1100325140/f6eeacd6 打开链接&#xff0c;里面的内容很详细。

013 OpenCV copyMakeBorder(padding)

目录 一、环境 二、原理 三、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、原理 cv.copyMakeBorder是OpenCV库中的一个函数&#xff0c;用于在图像周围添加边框&#xff08;padding&#xff09;。这个函数可以用于图…

C#中GDI+绘图应用(柱形图、折线图和饼形图)

目录 一、柱形图 1.示例源码 2.生成效果 二、折线图 1.示例源码 2.生成效果 三、饼形图 1.示例源码 2.生成效果 GDI绘制的一些常用的图形&#xff0c;其中包括柱形图、折线图和饼形图。 一、柱形图 柱形图也称为条形图&#xff0c;是程序开发中比较常用的一种图表技术…

掌握Python异步IO利器:深入解读Asyncio

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 异步 IO 与 Asyncio 在 Python 中的应用 在当今互联网时代&#xff0c;处理大量并发请求或I/O密集型任务对于软件应用程序至关重要。Python的异步IO框架——Asyncio成为了处理此类问题的有力工具。本文将深入介…

在vue中导出csv文件

在Vue中使用 xlsx 库将表格数据导出为CSV文件&#xff0c;你需要按照以下步骤操作&#xff1a; 安装 xlsx 库 如果你的项目中还没有安装xlsx库&#xff0c;可以使用npm或yarn来安装它。 npm install xlsx # 或者 yarn add xlsx在Vue组件中导入xlsx库&#xff1a; 在需要处理…