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…

典型的ETL使用场景

典型的ETL使用场景 ETL( Extract&#xff0c;Transform&#xff0c;Load)是一种用于数据集成和数据转换的常用技术。它主要用于从多个数据源中提取数据&#xff0c;对数据进行清洗、转换和整合&#xff0c;最后加载到目标系统中。ETL 的使用场景非常广泛&#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%。 处理抽象逻辑问题的一个入手点就是…

【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成为了处理此类问题的有力工具。本文将深入介…

基于SpringBoot实现的电影院售票系统

一、 系统架构 前端&#xff1a;html | jquery | bootstrap 后端&#xff1a;springboot | thymeleaf | spring-data-jpa 环境&#xff1a;jdk1.8 | mysql | maven 二、代码及数据库 三、功能介绍 01. 首页 02. 登录页 03. 管理端-电影列表 04. 管理端-添加电影 05. 管…

京东数据分析:2023年10月京东打印机行业品牌销售排行榜

鲸参谋监测的京东平台10月份打印机市场销售数据已出炉&#xff01; 10月份&#xff0c;打印机市场整体销售下滑。鲸参谋数据显示&#xff0c;今年10月份&#xff0c;京东平台打印机的销量将近60万&#xff0c;环比降低约2%&#xff0c;同比降低约5%&#xff1b;销售额为4.4亿&a…

模拟电路学习笔记(一)之芯片篇(持续更新)

模拟电路学习笔记&#xff08;一&#xff09;之芯片篇&#xff08;持续更新&#xff09; 1.CD4047BE芯片 CD4047是一种包含高电压的多谐振荡器&#xff0c;该器件的操作可以在两种模式下完成&#xff0c;分别是单稳态和非稳态。CD4047需要一个外部电阻器和电容器来决定单稳态…

开源项目CuteSqlite开发笔记(二):SQLite的架构

在开发CuteSqlite图形客户端的时候&#xff0c;需要用到SQL的语法解释&#xff0c;来对SQL语句进行优化。找了很多的SQL语法解释器&#xff0c;都不是十分满意&#xff0c;只有翻开Sqlite的源码&#xff0c;看看SQLite对SQL语句的解释过程&#xff0c;本文是翻译的官方文档。 官…

Twincat功能块使用经验总结

控制全局变量&#xff1a; //轴控制指令 bi_Power: BOOL; //使能 bi_Reset: BOOL; //复位 bi_Stop: BOOL; //停止 bi_JogForward: BOOL; //正向点动 bi_JogBackwards: BOOL; //反向点动 bi_MoveAdditive: BOOL; //增量位…

Anaconda创建虚拟环境以及Pycharm和Jupyter如何切换虚拟环境

文章目录 Anaconda创建管理虚拟环境0. 进入到终端1. 创建新环境2. 切换环境3. 删除环境4. 查询当前已有的环境 Pycharm切换虚拟环境0. 更换解析器1. 添加虚拟环境&#xff08;之前默认的是base环境&#xff09;2. 验证切换虚拟环境 Jupyter Notebook 切换虚拟环境1. 安装ipyker…

基于SSM框架的《超市订单管理系统》Web项目开发(第五天)供应商管理,增删改查

基于SSM框架的《超市订单管理系统》Web项目开发&#xff08;第五天&#xff09;供应商管理&#xff0c;增删改查 上一次我们实现了多表关联查询&#xff0c;还有分页显示数据的功能。还完善了用户管理这一模块。 因此今天我们需要完成的是供应商管理模块&#xff0c;这一模块…