红黑树简单模拟实现

news2025/5/25 18:38:30

  • 定义
  • 成员变量
  • 旋转
  • insert
    • 以234树的角度来待插入操作
    • 具体代码
  • 完整代码

我们前面实现了 二叉搜索树和 AVL树。
其中AVL树是二叉搜索树的改进,但是有些人觉得二叉树搜索的插入调整太频繁了,或者说平衡条件过于苛刻。
于是人们放松了左右子树高度差的限制,只需要确保没有一条路径会比其他路劲长出两倍,这就是所谓的红黑树。

定义

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

红黑树满足的性质:

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

满足这上面五个条件就确保了最长路径不会超过最短路径两倍。接下来我解释一下这是为什么。

首先看到34两点。满足了这两点之后,我们的理论最短路径就是全黑结点。
那最长路径呢?
因为要满足第四条,所以只能在全黑结点路径上插入红色结点来增长路径,又因为红色结点不能连续,因此最多插入等量的红色结点。这就确保了最长路径不会超过最短路径的两倍

那为什么根节点要是黑色结点呢?
假如根节点是红色结点,那么他只能插入黑色结点,这就导致了左右子树的黑色结点数量不同。因此根节点只能是黑色的。

成员变量

enum Color
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	Color _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K,V> Node;
public:

private:
	Node* _root = nullptr;
};

旋转

在前文AVL树中我们实现了左单旋和右单旋,那么这两种旋转能不能合并成一个函数呢?
注意到左单旋:
在这里插入图片描述
右单旋:
在这里插入图片描述
左单旋是将parent右子subR的左子树subRL接到parent的右边。
右单旋是将parent左子subL的右子树subLR接到parent的左边。

如此对称的行为理应可以合并:

void Rotate(Node* child)
{
	Node* parent = child->_parent;
	Node** childson[] = {  &(child->_right),&(child->_left) };
	Node** parentson[] = { &(parent->_left),&(parent->_right) };
	bool IsRight = (child == parent->_right);

	*(parentson[IsRight]) = *(childson[IsRight]);
	if (*(childson[IsRight]))
		(*(childson[IsRight]))->_parent = parent;

	*(childson[IsRight]) = parent;
	child->_parent = parent->_parent;
	parent->_parent = child;

	if (parent == _root)
	{
		_root = child;
		child->_parent = nullptr;
	}
	else 
	{
		//Node* grandparent = parent->_parent; 千错万错
		Node* grandparent = child->_parent;
		if (parent == grandparent->_left) grandparent->_left = child;
		else grandparent->_right = child;
	}

}

这时候如果要左单旋,就传入旋转点的右孩子,如果要右单旋就传入旋转点的左孩子。

insert

对于插入一个结点,那么是默认他为红色还是黑色呢?
如果默认是黑色就会违反所有路径黑色结点数相同这一个规定,如果默认是红色就有可能违反红色结点不连续这一规定。
显然插入红色结点的冲突比较好处理,因此我们默认插入的结点为红色结点。那么就分三种情况:

  1. 插入的结点是根节点,那只需要让根节点变黑即可
  2. 插入的结点的父结点是黑色结点,那么不违反规则,不做处理
  3. 插入的结点的父结点是红色结点,需要处理。

对于第三种情况,又要分两种情况进行讨论:

  1. 叔叔结点存在且为红色:

在这里插入图片描述

如上,10结点为插入结点。
首先是很容易想到的,为了维持到10结点这一路径的黑色结点个数不变,我们需要将20结点变黑同时将30结点变红,如果仅仅是将20结点变黑,那么到10结点这一路径的黑色结点数就会+1,违反规定4.
那么在30结点变红之后,到40结点这一路径的黑色节点数是不是就少了一个?所以我们要将40结点变黑。如下处理:
在这里插入图片描述
那么30结点有没有可能和他的父结点冲突呢?答案是有可能的,所以我们还需要将30结点看作插入结点继续循环处理。

  1. 那么再看到另一种情况,也就是叔叔结点不存在或者为黑色

实际上这种情况又详细分为四种,我们先看第一种插入结点为左子结点,叔叔结点为右子结点或者不存在:
在这里插入图片描述
由图可知,先将父亲变黑,爷爷变红然后进行右单旋调整

第二种插入结点为右子结点,叔叔结点为左子结点或者不存在,根据对称性可知,先将父亲变黑,爷爷变红然后进行左单旋调整

第三种插入结点为右子结点,叔叔结点为右子结点或者不存在
在这里插入图片描述
可以看到对父亲结点进行左单旋就转换成了第一种情况,这时按第一种情况除了

第四种插入结点为左子结点,叔叔结点为左子结点或者不存在

以234树的角度来待插入操作

我们可以将红黑树看作一棵二三四树,其中黑色结点为2结点,黑色结点加红色结点为3结点,黑色结点和两个红色结点为4结点。
例如:
在这里插入图片描述
这时我们插入一个16:
在这里插入图片描述
这就对应了父结点时黑色结点,不用处理

如果插入一个3:
在这里插入图片描述
这时候对应叔叔结点存在且为红。
那么5结点上溢,叔父变黑,爷爷变红,继续向上处理。

如果插入一个17:
在这里插入图片描述
本质上就是父亲做了爷爷,又因为父亲是右子结点,实际上就是进行左单旋。对应插入结点为右子结点,叔叔结点为左子结点或者不存在。

我们再看到如果插入25.5:
在这里插入图片描述
实际上是儿子做了爷爷,首先儿子要当爹,又因为儿子是左子结点,因此右单旋。然后儿子就是爷爷的右子结点了,再左单旋就成了爷爷。最后变色处理。
对应插入结点为左子结点,叔叔结点为左子结点或者不存在。

其余情况也是类似讨论,这里不多做赘述。

具体代码

bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}

	cur = new Node(kv);
	cur->_col = RED; // 新增节点给红色
	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;


	//父亲颜色是黑色结束
	while (parent&&parent->_col == RED)
	{

		//是否一定有爷爷?肯定的,因为父亲结点是红色,因此不是根节点。
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续处理
				cur = grandfather;
				//未必有parent
				parent = cur->_parent;
			}
			else//叔叔不存在或者为黑
			{

				if (cur == parent->_left)
				{
					//RotateR(grandfather);
					Rotate(grandfather->_left);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{

					//RotateL(parent);
					//RotateR(grandfather);
					Rotate(parent->_right);
					Rotate(grandfather->_left);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}

		}
		else
		{
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续处理
				cur = grandfather;
				//未必有parent
				parent = cur->_parent;
			}
			else//叔叔不存在或为黑
			{
				if (cur == parent->_right)
				{
					//RotateL(grandfather);
					Rotate(grandfather->_right);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}

	}

	//处理边界情况
	_root->_col = BLACK;

	return true;
}

完整代码

完整代码包括InOrder和IsBalance

enum Color
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	Color _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K,V> Node;
public:

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED; // 新增节点给红色
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;


		//父亲颜色是黑色结束
		while (parent&&parent->_col == RED)
		{

			//是否一定有爷爷?肯定的,因为父亲结点是红色,因此不是根节点。
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续处理
					cur = grandfather;
					//未必有parent
					parent = cur->_parent;
				}
				else//叔叔不存在或者为黑
				{

					if (cur == parent->_left)
					{
						//RotateR(grandfather);
						Rotate(grandfather->_left);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{

						//RotateL(parent);
						//RotateR(grandfather);
						Rotate(parent->_right);
						Rotate(grandfather->_left);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}

			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续处理
					cur = grandfather;
					//未必有parent
					parent = cur->_parent;
				}
				else//叔叔不存在或为黑
				{
					if (cur == parent->_right)
					{
						//RotateL(grandfather);
						Rotate(grandfather->_right);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}

		}

		//处理边界情况
		_root->_col = BLACK;

		return true;
	}

	void Rotate(Node* child)
	{
		Node* parent = child->_parent;
		Node** childson[] = {  &(child->_right),&(child->_left) };
		Node** parentson[] = { &(parent->_left),&(parent->_right) };
		bool IsRight = (child == parent->_right);

		*(parentson[IsRight]) = *(childson[IsRight]);
		if (*(childson[IsRight]))
			(*(childson[IsRight]))->_parent = parent;

		*(childson[IsRight]) = parent;
		child->_parent = parent->_parent;
		parent->_parent = child;

		if (parent == _root)
		{
			_root = child;
			child->_parent = nullptr;
		}
		else 
		{
			//Node* grandparent = parent->_parent; 千错万错
			Node* grandparent = child->_parent;
			if (parent == grandparent->_left) grandparent->_left = child;
			else grandparent->_right = child;
		}

	}

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

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

	//	subL->_right = parent;

	//	Node* ppNode = parent->_parent;
	//	parent->_parent = subL;

	//	if (parent == _root)
	//	{
	//		_root = subL;
	//		_root->_parent = nullptr;
	//	}
	//	else
	//	{
	//		if (ppNode->_left == parent)
	//		{
	//			ppNode->_left = subL;
	//		}
	//		else
	//		{
	//			ppNode->_right = subL;
	//		}

	//		subL->_parent = ppNode;
	//	}
	//}

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

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

	//	subR->_left = parent;
	//	Node* ppNode = parent->_parent;

	//	parent->_parent = subR;

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

	bool IsBalance()
	{
		if (_root->_col == RED)
		{
			return false;
		}

		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				refNum++;

			cur = cur->_left;
		}

		return Check(_root,0, refNum);
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	bool Check(Node* root,int blackNum,const int refNum)
	{
		if (root == nullptr)
		{
			if (blackNum != refNum)
			{
				cout << "黑错" << endl;
				return false;
			}
			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "红红" << endl;
			return false;//连续红
		}

		if (root->_col == BLACK)
		{
			blackNum++;
		}

		return Check(root->_left, blackNum, refNum) && Check(root->_right, blackNum, refNum);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}


	Node* _root = nullptr;
	//size_t _size;
};

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

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

相关文章

豪越科技:消防应急装备智能仓储管理新变革

在消防救援工作中&#xff0c;消防装备无疑是消防员们与火灾等灾害顽强对抗的关键“武器”。然而&#xff0c;传统的消防装备管理模式长期以来饱受诸多痛点的困扰&#xff0c;严重影响着消防工作的高效开展和救援效果。 在过去&#xff0c;装备丢失的情况时有发生。由于缺乏有效…

如何设计Agent的记忆系统

最近看了一张画Agent记忆分类的图 我觉得分类分的还可以&#xff0c;但是太浅了&#xff0c;于是就着它的逻辑&#xff0c;仔细得写了一下在不同的记忆层&#xff0c;该如何设计和选型 先从流程&#xff0c;作用&#xff0c;实力和持续时间的这4个维度来解释一下这几种记忆&am…

毕业论文格式(Word)

目录 Word目录怎么自动生成&#xff1f;快速生成试试这3个方法&#xff01; - 知乎https://zhuanlan.zhihu.com/p/692056836目录生成需要先设置标题样式&#xff0c;这个不仅是目录生成需要&#xff0c;和后续的图表也有关系。 最好不要自己创建新的样式&#xff0c;而是在现有…

学习STC51单片机14(芯片为STC89C52RC)

接下来我们进入学会了HC—SR04 还有舵机那么现在我们将他们融合在一起&#xff0c;用超声波来引导舵机的转动 我们这个最后的成果是做一个智能垃圾桶 成品是这样的&#xff0c;是不是可有意思了 成品视频 现在我们将舵机的代码和超声波测距模块的代码整合到一起&#xff0c;实…

基于CodeBuddy实现本地网速的实时浏览小工具

本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 前言 在数字化浪潮席卷全球的今天&#xff0c;网络已成为人们生活和工作中不可或缺的基础设施。无论是在线办公、学习、娱乐&#xff0c;还是进行大数据传输和云计算&…

stable diffusion论文解读

High-Resolution Image Synthesis with Latent Diffusion Models 论文背景 LDM是Stable Diffusion模型的奠基性论文 于2022年6月在CVPR上发表 传统生成模型具有局限性&#xff1a; 扩散模型&#xff08;DM&#xff09;通过逐步去噪生成图像&#xff0c;质量优于GAN&#x…

计算机网络(3)——传输层

1.概述 1.1 传输层的服务和协议 (1)传输层为允许在不同主机(Host)上的进程提供了一种逻辑通信机制 (2)端系统(如手机、电脑)运行传输层协议 发送方&#xff1a;将来自应用层的消息进行封装并向下提交给 网络层接收方&#xff1a;将接收到的Segment进行组装并向上提交给应用层 …

LangChain构建RAG的对话应用

目录 Langchain是什么&#xff1f; LangSmith是什么&#xff1f; ​编辑 使用Python构建并使用AI大模型 数据解析器 提示模版 部署 记忆功能 Chat History -- 记忆 代码执行流程&#xff1a; 流式输出 构建向量数据库和检索器 检索器 代码执行流程 LLM使用检索器…

目标检测DN-DETR(2022)详细解读

文章目录 gt labels 和gt boxes加噪query的构造attention maskIS&#xff08;InStability&#xff09;指标 在DAB-Detr的基础上&#xff0c;进一步分析了Detr收敛速度慢的原因&#xff1a;二分图匹配的不稳定性&#xff08;也就是说它的目标在频繁地切换&#xff0c;特别是在训…

嵌入式培训之系统编程(四)进程

一、进程的基本概念 &#xff08;一&#xff09;定义 进程是一个程序执行的过程&#xff08;也可以说是正在运行的程序&#xff09;&#xff0c;会去分配内存资 源&#xff0c;cpu的调度&#xff0c;它是并发的 &#xff08;二&#xff09;PCB块 1、PCB是一个结构体&#x…

天文数据处理:基于CUDA的射电望远镜图像实时去噪算法(开源FAST望远镜数据处理代码解析)

一、射电天文数据处理的挑战与CUDA加速的必要性 作为全球最大的单口径射电望远镜&#xff0c;中国天眼&#xff08;FAST&#xff09;每秒产生38GB原始观测数据&#xff0c;经预处理后生成数千万张图像。这些数据中蕴含的脉冲星、中性氢等天体信号常被高斯白噪声、射频干扰&…

VS编码访问Mysql数据库

安装 MySQL Connector/C 的开发包 libmysqlcppconn-dev是 MySQL Connector/C 的开发包&#xff0c;它的主要用途是让 C 开发者能够方便地在应用程序中与 MySQL 数据库进行交互。它提供了以下功能&#xff1a; 数据库连接&#xff1a;通过标准的 C 接口连接到 MySQL 数据库。S…

一周学会Pandas2 Python数据处理与分析-Pandas2数据合并与对比-pd.merge():数据库风格合并

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili pd.merge()&#xff1a;数据库风格合并 **核心功能**&#xff1a;基于列值&#xff08;类似 SQL JOIN&#xff09;合…

CodeBuddy 实现图片转素描手绘工具

本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 前言 最近在社交媒体上&#xff0c;各种素描风格的图片火得一塌糊涂&#xff0c;身边不少朋友都在分享自己的 “素描照”&#xff0c;看着那些黑白线条勾勒出的独特韵味&a…

3.8.2 利用RDD计算总分与平均分

在本次实战中&#xff0c;我们利用Spark的RDD完成了成绩文件的总分与平均分计算任务。首先&#xff0c;准备了包含学生成绩的文件并上传至HDFS。接着&#xff0c;通过交互式方式逐步实现了成绩的读取、解析、总分计算与平均分计算&#xff0c;并最终输出结果。此外&#xff0c;…

29-FreeRTOS事件标志组

一、概述 事件是一种实现任务间通信的机制&#xff0c;主要用于实现多任务间的同步&#xff0c;但事件通信只能是事件类型的通信&#xff0c;无数据传输。与信号量不同的是&#xff0c;它可以实现一对多&#xff0c;多对多的同步。 即一个任务可以等待多个事件的发生&#xff1…

「EMD/EEMD/VMD 信号分解方法 ——ECG信号处理-第十四课」2025年5月23日

一、引言 上一节&#xff0c;我们介绍了希尔伯特黄变换&#xff08;HHT&#xff09;及其经验模态分解&#xff08;EMD&#xff09;的相关内容&#xff0c;这一节&#xff0c;我们继续拓展EMD分解技术&#xff0c;补充介绍集合经验模态分解&#xff08;Ensemble Empirical Mode …

二叉树层序遍历6

INT_MIN的用法&#xff1a; INT_MIN是C/C 中的一个宏常量 &#xff0c;在 <limits.h> &#xff08;C 中也可使用 <climits> &#xff09;头文件中定义&#xff0c;代表 int 类型能表示的最小整数值 。其用法主要体现在以下方面&#xff1a; 1.初始化变量 …

【论文精读】2023 AAAI--FastRealVSR现实世界视频超分辨率(RealWorld VSR)

文章目录 一、摘要二、Method2.1 现象&#xff08;问题&#xff09;--对应文中隐状态的分析&#xff08;Analysis of Hidden State&#xff09;2.2 怎么解决 --对应文中Framework2.2.1 整体流程&#xff1a;2.2.2 HSA模块怎么工作&#xff1f;2.2.2.1 隐藏状态池2.2.2.2 选择性…

IPython 常用魔法命令

文章目录 IPython 魔法命令&#xff08;Magic Commands&#xff09;一、系统与文件操作1. %ls2. %cd​​和%pwd3. %%writefile​​4. %run 二、性能分析与计时1. %timeit2. %prun​​3. ​​%%timeit 三、代码处理与交互1. %load2. ​​%edit3. ​​%store 四、调试与诊断2. ​…