【C++杂货铺】红黑树


目录

🌈前言🌈 

📁 红黑树的概念

📁 红黑树的性质

📁 红黑树节点的定义

📁 红黑树的插入操作

📁 红黑树和AVL树的比较

📁 全代码展示

📁 总结


🌈前言🌈 

        欢迎大家观看本期【C++杂货铺】,本期内容将讲解二叉搜索树的进阶——红黑树。红黑树是一种二叉搜索树,通过控制每个节点的颜色,来调整整棵树的高度。

        红黑树是set和map实现的底层实现,在下一期内容,我们将讲解STL中set和map的模拟实现。如果你对二叉搜索树还不是很了解,可以快速阅览下面这篇文章;

【C++杂货铺】二叉搜索树-CSDN博客

📁 红黑树的概念

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

📁 红黑树的性质

1. 每个节点不是红色就是黑色;

2. 根节点是黑色的;

3. 如果一个节点是红色的,那么它的两个孩子节点是黑色的;

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

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

📁 红黑树节点的定义

// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
 RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
     , _data(data), _color(color)
 {}
 RBTreeNode<ValueType>* _pLeft;   // 节点的左孩子
 RBTreeNode<ValueType>* _pRight;  // 节点的右孩子
 RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给
出该字段)
 ValueType _data;            // 节点的值域
 Color _color;               // 节点的颜色
};

📁 红黑树的插入操作

        红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索树规则插入新节点;

        新插入节点的默认颜色是红色。因为红色容易控制规则,如果默认插入黑色,需要保证从当前节点到叶子节点具有相同的黑色节点个数,不易控制。

        即先保证性质4不被违反,再来判断性质3是否被违反,如果违反就进行调整。

2. 检测新节点插入后,红黑树的性质是否遭到破坏。

        如果双亲节点的颜色是黑色,没有违反规则,则不需要调整;当新插入节点的双亲节点颜色为红色时,就违反了性质3,即不能有连续在一起的红色节点,此时需要进行调整,可分为两种情况:

约定:cur为当前节点,p为父亲节点,g为祖父节点,u为叔叔节点

● 情况1:cur为红,p为红,g为黑,u存在且为红

● 情况2:cur为红,p为红,g为黑,u存在且为黑 或者 u不存在

        当如子树如下图所示时,需要对红黑树进行双旋,先以p为根进行左旋,再以g为根进行右旋。下图是p在g的左子树的情况,考虑一下p在g的右子树,且cur为p的左子树的情况。

📁 红黑树和AVL树的比较

        红黑树和AVL数都是搞笑的平衡二叉树,增删查改的时间复杂度都是O(log N),红黑树不追求绝对平衡,其只需要保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL数更优,而且红黑树的实现比较简单,所以实际应用中红黑树更多。

        map和set的底层就是红黑树。

📁 全代码展示

template <class T>
struct RBTreeNode
{
	typedef RBTreeNode<T> Node;

	RBTreeNode(const T& val = T())
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _val(val)
		, _color(RED)
	{}

	Node* _left;
	Node* _right;
	Node* _parent;
	T _val;
	Color _color;
};

template<class T>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	bool Insert(const T& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			_root->_color = Black;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_val > val)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_val < val)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(val);
		if (parent->_val < val)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;

		//调整颜色,不能出现连续的红色
		while (parent && parent->_color == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//叔叔在右边
				//1. 叔叔存在,且为红色
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_color == RED)
				{
					grandfather->_color = RED;
					uncle->_color = parent->_color = Black;
					cur = grandfather;
					parent = cur->_parent;
				}
				//2. 叔叔存在,且为黑色  ||  叔叔不存在
				else
				{
					/*
							g
						p		u
						c  
					*/
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_color = Black;
						grandfather->_color = RED;
					}
					/*
							g
						p		u
						    c
					*/
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_color = Black;
						grandfather->_color = RED;
					}
					break;
				}
			}
			else
			{
				//叔叔在左边
				//1. 叔叔存在,且为红色
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_color == RED)
				{
					grandfather->_color = RED;
					parent->_color = uncle->_color = Black;
					cur = grandfather;
					parent = cur->_parent;
				}
				//2. 叔叔存在,且为黑色  ||  叔叔不存在
				else
				{
					/*
							g
						u		p
						            c
					*/
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_color = Black;
						grandfather->_color = RED;
					}
					/*
							g
						u		p
							    c   
					*/
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_color = Black;
						grandfather->_color = RED;
					}
					break;
				}
			}
		}

		_root->_color = Black;

		return true;
	}

//遍历
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
	
bool isBalance()
{
	if (_root == nullptr)
	{
		return true;
	}

	int BlackNum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_color == Black)
			BlackNum++;
		cur = cur->_right;
	}

	return _isBalance(_root,0,BlackNum);
}

protected:
	bool _isBalance(Node* root,int count , const int& BlackNum)
	{
		if(root == nullptr)
		{
			if (count != BlackNum)
				return false;
			return true;
		}
		if (root->_color == RED && root->_parent->_color == RED)
		{
			cout << "red" << endl;
			return false;
		}
		if (root->_color == Black)
			count++;
		return _isBalance(root->_left,count,BlackNum)
			&& _isBalance(root->_right,count,BlackNum);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
			
		_InOrder(root->_left);
		cout << root->_val << endl;
		_InOrder(root->_right);
	}

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		Node* pparent = parent->_parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == pparent->_right)
			{
				pparent->_right = subR;
			}
			else
			{
				pparent->_left = subR;
			}
			subR->_parent = pparent;

		}
	}

	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		Node* pparent = parent->_parent;
		parent->_parent = subL;
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == pparent->_right)
			{
				pparent->_right = subL;
			}
			else
			{
				pparent->_left = subL;
			}
			subL->_parent = pparent;

		}
	}
private:
	Node* _root = nullptr;
};

📁 总结

        以上就是本期【C++杂货铺】的主要内容了,讲解了红黑树如果优化二叉搜索树,红黑树的概念,红黑树实现插入,以及红黑树的高度平衡调整,此外模拟实现了红黑树。

        如果感觉本期内容对你有帮助,欢迎点赞,收藏,关注Thanks♪(・ω・)ノ

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mybatis-plus(2)

上文我们介绍完mybatis-plus的常用注解&#xff0c;现在介绍 mp的基础的yaml配置 mybatis-plus:type-aliases-package: #该位置写 数据库对应实体类的全路径global-config:db-config:id-type: auto # 全局id类型为自增长 mp同时也是支持手写sql&#xff0c;而且mapper的读取地…

OpenMVS学习笔记(一):WSL编译安装测试

1.CUDA和CUDNN安装 [1] WSL版本cuda安装&#xff1a; >> wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin >> sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600 >> wg…

weblogic 反序列化 [CVE-2017-10271]

一、漏洞描述 这个漏洞是wls-wsat这个接口出了问题&#xff0c;Weblogic的WLS Security组件对外提供webservice服务&#xff0c;其中使用了XMLDecoder来解析用户传入的XML数据&#xff0c;在解析的过程中出现反序列化漏洞&#xff0c;导致可执行任意命令。攻击者发送精心构造的…

【Unity从零开始学习制作手机游戏】第01节:控制3D胶囊体运动

1. 新建Project L01 使用3D Mobile模板。 2. 建立一个平面&#xff0c;用来承载物体 3. 导入Unity库内的胶囊体 下载 StandardAssets https://download.unitychina.cn/download_unity/e80cc3114ac1/WindowsStandardAssetsInstaller/UnityStandardAssetsSetup-5.6.7f1.exe …

Abaqus显示单元面的编号

注意&#xff1a;这里为了显示单元的面编号&#xff0c;而不是‘Part’的面。对于六面体单元有六个面&#xff0c;编号从1-6&#xff0c;对于四面体单元有四个面&#xff0c;编号从1-4。 1、要显示单元面的编号首先要进入‘Visualization’模块&#xff0c;如下图&#xff1a;…

Jmeter 性能-阶梯负载最终请求数

1、设置阶梯加压线程组请求参数 说明&#xff1a; 每隔2秒钟&#xff0c;会在1秒内启动5个线程 每次线程加载之后都会运行2s然后开始下一次线程加载 最终会加载50个线程并持续运行30s 50个线程持续运行30s后&#xff0c;会每隔2秒钟停止5个线程&#xff0c;剩余的线程继续负…

数据结构与算法-排序算法1-冒泡排序

本文先介绍排序算法&#xff0c;然后具体写冒泡排序。 目录 1.排序算法简介 2.常见的排序算法分类如下图&#xff1a; 3.冒泡排序&#xff1a; 1.介绍&#xff1a; 2.动态图解 3.举例 4.小结冒泡排序规则 5.冒泡排序代码 6.优化 7.优化后时间 代码&#xff1a; 运…

数据库系统概论(个人笔记)(第一部分)

数据库系统概论&#xff08;个人笔记&#xff09; 文章目录 数据库系统概论&#xff08;个人笔记&#xff09;1、介绍1.1 数据库系统应用1.2 数据库系统的历史1.3 数据库系统的目标**大学数据库例子**1.4 数据视图1.5 数据库语言1.6 数据库设计1.7 数据库引擎1.8 数据库体系结构…

2023年上半年信息系统项目管理师——综合知识真题与答案解释(4)

2023年上半年信息系统项目管理师 ——综合知识真题与答案解释(4) 61、文档的规范化管理主要体现在&#xff08;&#xff09;方面。 ①文档书写规范 ②文档质量级别 ③图表编号规则 ④文档目录编写标准 ⑤文档管理制度 ⑥文档安全标准 A&#xff0e;①②③④ B&#xff0e;②③…

MySQL_DDL语句

1.Data类临时数据的弊端 我们之前在将ServletJSP配合处理请求的过程中 数据库起到一个存取数据的作用 但是我们之前的案例中 数据是在Data类中临时定义的 并不是从数据库中获取的 这样做是不好的 因为每一次服务器关闭之后 那么部署在其上的类也会随着卸载 紧接着和类相挂钩的静…

ms17-010(永恒之蓝)

1.漏洞介绍: 永恒之蓝&#xff08;ms17-010&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry勒索病…

计算机网络(第八版 谢希仁 编著) 期末复习大纲

一.每章总结 第一章&#xff1a;分组交换&#xff0c;计网定义、范围划分&#xff0c;性能指标&#xff0c;五层体系结构&#xff0c;TCP/IP体系结构 第二章&#xff1a;物理层&#xff0c;码元&#xff0c;基带调制(数字信号->数字信号&#xff0c;也叫编码)&#xff0c;带…

SSE介绍(实现流式响应)

写在前面 本文一起来看下SSE相关内容。 1&#xff1a;SSE是什么 全称&#xff0c;server-send events&#xff0c;基于http协议&#xff0c;一次http请求&#xff0c;server端可以分批推送数据&#xff0c; 不同于websocket的全双工通信&#xff0c;SSM单向通信,一般应用于需…

softmax函数与交叉熵损失详解

文章目录 一、softmax函数1.1 引入指数形式的优点1.2 引入指数形式的缺点 二、交叉熵损失函数2.1 交叉熵损失函数2.2 softmax与交叉熵损失 参考资料 一、softmax函数 softmax用于多分类过程中&#xff0c;它将多个神经元的输出&#xff0c;映射到&#xff08;0,1&#xff09;区…

simulink-仿真以及PID参数整定/PID tuner 的使用流程

控制器搭建与参数整定 搭建一个前馈PID控制器控制系统PID tuner使用 一个懂点控制但不多的小白&#xff0c;因为需要利用simulink仿真&#xff0c;所以不得不学习一些仿真的知识&#xff0c;这篇文章适合和我一样的新手入门&#xff0c;有理解错误的地方希望大手们能够指出来共…

背完这些软件测试核心面试题,offer轻松拿捏了!

你赞同过 软件测试和开发 相关内容 01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和…

spacy微调BERT-NER模型

数据准备 加载数据集 from tqdm.notebook import tqdm import osdataset [] with open(train_file, r) as file:for line in tqdm(file.readlines()):data json.loads(line.strip())dataset.append(data)你可以按照 CLUENER 的格式准备训练数据&#xff0c; 例如&#xff1…

彻底搞定找不到msvcp100.dll,无法继续执行代码的问题

当您在使用电脑过程中遇到程序运行异常&#xff0c;提示“缺失msvcp100.dll文件”时&#xff0c;不必过于焦虑&#xff0c;此问题可通过一系列简单步骤得到有效解决。MSVCP100.dll是Microsoft Visual C库的一部分&#xff0c;主要用于支持某些应用程序运行所需的特定功能。如果…

ohmyzsh的安装过程中失败拒绝连接问题的解决

1.打开官网Oh My Zsh - a delightful & open source framework for Zsh 在官网能看到下面的界面 有这两种自动安装的方式 个人本次选择的是: wget https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O - 1.打开终端输入安装的指令 sh -c "$(wget…

(done) 什么是马尔可夫链?Markov Chain

参考视频&#xff1a;https://www.bilibili.com/video/BV1ko4y1P7Zv/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 如下图所示&#xff0c;马尔可夫链条实际上就是 “状态机”&#xff0c;只不过状态机里不同状态之间的边上是 “…