红黑树模拟实现

news2024/9/20 7:52:33
概念

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

下面就是一个红黑树: 

注:从根节点到NIL节点才是才算是一条路径。

性质

1.每个结点不是红色就是黑色

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

节点定义
enum Colour
{
	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;
	Colour _col;

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

 通过上面的代码不难发现,我们将新创建的节点设置为红色,为什么不设置成黑色呢?

根据红黑树的性质4,我们知道,每条路径上黑色节点的数目是相等的,倘若我们在插入节点时,插入的是黑色节点,那么将影响整棵树;插入的是红色节点,不至于影响整棵树,因为如果新插入节点的父节点是黑色,将不需要进行调整;如果新插入节点的父节点是红色,只需要进行相关调整即可。

总的来说,新插入节点是红色,影响范围更小,利于编码实现,更方便。

红黑树的插入

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

2.判断红黑树的结构是否被破坏。

因为新节点的默认颜色是红色,因此:如果其父节点是黑色,就没有违反红黑树任何行者,则不需要调整;但当新插入节点的父节点是红色时,就违反了性质三(不能出现连续的红色节点),此时需要对红黑树分情况来讨论:

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

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

解决方式:将p,u改为黑,g改为红,然后把g改为cur,继续向上调整。

情况二:cur为红,p为红,g为黑,u不存在 / u存在且为黑

解决方式:p为g的左孩子,cur为p的左孩子,则进行右单旋,p改为黑色,g改为红色;

                  p为g的右孩子,cur为p的右孩子,则进行左单旋,p改为黑色,g改为红色。

此时u有两种情况:

1.u节点不存在,此时cur一定是新增节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色的,就不满足性质四(每条路径黑色节点个数相同)。

2.u节点存在,则其一定是黑色的.

情况三:cur为红,p为红,g为黑,u不存在 / u存在且为黑

解决方式:p为g的左孩子,cur为p的右孩子,则进行左单旋,再右单旋,  p改为黑色,g改为红色;

                  p为g的右孩子,cur为p的左孩子,则进行右单旋,再左单旋,  p改为黑色,g改为红色。

此时u有两种情况:

1.u节点不存在,此时cur一定是新增节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色的,就不满足性质四(每条路径黑色节点个数相同)。

2.u节点存在,则其一定是黑色的.

插入代码实现 

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;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}

	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			//     g
			//   p   u
			// c
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上更新处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_left)
				{
					// 单旋
					//     g
					//   p
					// c
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					// 双旋
					//     g
					//   p
					//     c
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else  // parent == grandfather->_right
		{
			//     g
			//   u   p 
			//          c
			//
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				// 变色
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//     g
					//   u   p 
					//     c
					//
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}

	_root->_col = BLACK;

	return true;
}
完整代码
#pragma once

enum Colour
{
	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;
	Colour _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;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//     g
				//   p   u
				// c
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上更新处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						// 单旋
						//     g
						//   p
						// c
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						// 双旋
						//     g
						//   p
						//     c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else  // parent == grandfather->_right
			{
				//     g
				//   u   p 
				//          c
				//
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   u   p 
						//     c
						//
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}

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

		parent->_right = subRL;
		subR->_left = parent;

		Node* parentParent = parent->_parent;

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

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

			subR->_parent = parentParent;
		}
	}

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

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

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = parentParent;
		}
	}

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

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

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

	// 根节点->当前节点这条路径的黑色节点的数量
	bool Check(Node* root, int blacknum, const int refVal)
	{
		if (root == nullptr)
		{
			//cout << balcknum << endl;
			if (blacknum != refVal)
			{
				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, refVal)
			&& Check(root->_right, blacknum, refVal);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
			return false;

		//参考值
		int refVal = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refVal;
			}

			cur = cur->_left;
		}

		int blacknum = 0;
		return Check(_root, blacknum, refVal);
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

使用GZip对npm run build打包的vendor.js文件进行压缩

vue-cli项目 安装npm i compression-webpack-plugin -D npm i compression-webpack-plugin -D使用&#xff1a;在vue.config.js文件中 const CompressionPlugin require(compression-webpack-plugin) module.exports {configureWebpack: {plugins: [new CompressionPlugin…

html+css+js贪吃蛇游戏

贪吃蛇游戏&#x1f579;四个按钮控制方向&#x1f3ae; 源代码在图片后面 点赞❤️关注&#x1f64f;收藏⭐️ 互粉必回&#x1f64f;&#x1f64f;&#x1f60d;&#x1f60d;&#x1f60d; 源代码&#x1f4df; <!DOCTYPE html> <html lang"en"&…

【2024最新】Arduino通过Python进行串口通信控制电机

1. 背景 最近想研究一下用 Python 控制 Arduino 的技术&#xff0c;通过上网查询&#xff0c;发现可以用 Python 中的 serial 库来实现和 Arduino 主板的串口通信&#xff0c;从而控制 Arduino。 特此记录一下这个小项目的过程及出现的问题。 2. 基础准备 主板&#xff1a;…

中仕公考:“三支一扶”岗位分别做什么工作?

“三支一扶”计划旨在招募应届毕业生或近两年内毕业的毕业生&#xff0c;部分省份还考虑技工院校高级工班、预备技师班毕业生。在湖北省&#xff0c;报考支医岗位不限制毕业年限&#xff0c;安徽和云南等省对支医类岗位取消了开考比例要求。为解决招人留人难题&#xff0c;艰苦…

毛绒玩具音乐芯片:OTP语音芯片WTN6040方案解析

随着科技的不断发展&#xff0c;智能化和互动性已经成为玩具设计中的关键因素。在毛绒玩具市场中&#xff0c;集成音乐播放功能的毛绒玩具因其趣味性和互动性而备受欢迎。本文将详细介绍OTP&#xff08;One Time Programmable&#xff09;语音芯片WTN6040在毛绒玩具音乐芯片中的…

网页UI:被客户说不大气!大气能当饭吃?真能,最起码保住你饭碗

大气的网页UI设计可以带来以下几个好处&#xff1a; 提升品牌形象&#xff1a;大气的设计能够给用户留下深刻的印象&#xff0c;增强品牌的认知度和形象。通过精心设计的元素、色彩和排版&#xff0c;可以传达出品牌的专业、高端和可信赖的形象。强化用户体验&#xff1a;大气…

SpringBoot开发实用篇(二)

目录 一&#xff1a;Redis 1&#xff1a;SpringBoot整合Redis 2&#xff1a;SpringBoot读写Redis的客户端 3&#xff1a;SpringBoot操作Redis实现技术切换&#xff08;jedis&#xff09; 二&#xff1a;Mongodb 1&#xff1a;Mongodb基础操作 2&#xff1a;SpringBoot整合…

【DataSophon】DataSophon1.2.1 ranger usersync整合

目录 一、简介 二、实现步骤 2.1 ranger-usersync包下载编译 2.2 构建压缩包 2.3 编辑元数据文件 2.4 修改源码 三、重新安装 一、简介 如下是DDP1.2.1默认有的rangerAdmin&#xff0c; 我们需要将rangerusersync整合进来 ,实现将Linux机器上的用户和组信息同步到Ranger…

『粽享端午』交互小程序 小游戏 案例赏析

在这片古老而又年轻的土地上&#xff0c;地域的差异孕育了丰富多彩的饮食文化。粽子&#xff0c;作为端午节的象征&#xff0c;承载着南咸北甜的口味之争&#xff0c;自古便在人们舌尖上演绎着不同的风味传奇。 然而&#xff0c;在快节奏的现代生活洪流中&#xff0c;我们渐渐失…

家谱管理系统

《家谱管理系统》 一个家谱关系由若干家谱记录构成&#xff0c;每个家谱记录由父亲、母亲和子女姓名构成&#xff0c;其中姓名是关 键字。设计并实现一个简单的家谱管理系统。定义一个主菜单&#xff0c;界面友好&#xff0c;演示程序以用户和计算机的对话方式进行&#xff0c…

开关电源——调制模式和工作模式

一、开关电源的调制模式 开关电源作为一种广泛应用于电子设备中&#xff0c;用于将一定电压和电流转换为另一种电压和电流的技术&#xff0c;以下是开关电源三种常见的调制模式&#xff1a; 脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09; 脉冲频率调制&#xff…

触发器编程-创建(CREATE TRIGGER)、删除(DROP TRIGGER)

一、定义 1、触发器&#xff08;Trigger&#xff09;是用户对某一表中的数据做插入、更新和删除操作时被处罚执行的一段程序&#xff0c;通常我们使用触发器来检查用户对表的操作是否合乎整个应用系统的需求&#xff0c;是否合乎商业规则以维持表内数据的完整性和正确性 2、一…

从nginx返回404来看http1.0和http1.1的区别

序言 什么样的人可以称之为有智慧的人呢&#xff1f;如果下一个定义&#xff0c;你会如何来定义&#xff1f; 所谓智慧&#xff0c;就是能区分自己能改变的部分&#xff0c;自己无法改变的部分&#xff0c;努力去做自己能改变的&#xff0c;而不要天天想着那些无法改变的东西&a…

AI视频教程下载-使用ChatGPT成为全栈JavaScript开发者

学习使用Express JS和React JS进行全栈JavaScript开发 ChatGPT Express JS MongoDB React JS Tailwind 解锁全栈网页开发的世界&#xff0c;我们为初学者和中级学习者设计了全面的课程。在这段沉浸式的旅程中&#xff0c;你将深入前端和后端开发的基本概念&#xff0c;为自…

everything高级搜索-cnblog

everything高级搜索用法 基础4选项验证 总结搜索方式 高级搜索搜指定路径文件名: 文件名 路径不含文件名: &#xff01;文件名包含单词 路径包含指定内容: 路径 content:内容 大小写 区分大小写搜索搜指定路径文件名: case:文件名 路径全字匹配 全字搜指定路径文件名: wholewo…

【网络安全】实验六(网络安全协议的应用SSL,Ipsec)

一、实验目的 二、搭配环境 打开两台虚拟机&#xff0c;并参照下图&#xff0c;搭建网络拓扑环境&#xff0c;要求两台虚拟机的IP地址要按照图中的标识进行设置&#xff0c;并根据搭建完成情况&#xff0c;勾选对应选项。同时&#xff0c;按照多选题中2-3题的要求完成相关环境…

java核心-泛型

目录 概述什么是泛型分类泛型类泛型接口泛型方法 泛型通配符分类 泛型类型擦除分类无限制类型擦除有限制类型擦除 问题需求第一种第二种 概述 了解泛型有利于学习 jdk 、中间件的源码&#xff0c;提升代码抽象能力&#xff0c;封装通用性更强的组件。 什么是泛型 在定义类、接…

web安全基础名词概念

本节内容根据小迪安全讲解制作 第一天 域名&#xff1a; 1.1什么是域名&#xff1f; 网域名称(英语&#xff1a;Domain Name&#xff0c;简称&#xff1a;Domain)&#xff0c;简称域名、网域&#xff0c;是由一串用点分隔的字符组成的互联网上某一台计算机或计算机组的名称&a…

YoloV9改进策略:Block改进|轻量实时的重参数结构|最新改进|即插即用(全网首发)

摘要 本文使用重参数的Block替换YoloV9中的RepNBottleneck&#xff0c;GFLOPs从239降到了227&#xff1b;同时&#xff0c;map50从0.989涨到了0.99&#xff08;重参数后的结果&#xff09;。 改进方法简单&#xff0c;只做简单的替换就行&#xff0c;即插即用&#xff0c;非常…

Python酷库之旅-第三方库Pandas(009)

目录 一、用法精讲 19、pandas.read_xml函数 19-1、语法 19-2、参数 19-3、功能 19-4、返回值 19-5、说明 19-6、用法 19-6-1、数据准备 19-6-2、代码示例 19-6-3、结果输出 20、pandas.DataFrame.to_xml函数 20-1、语法 20-2、参数 20-3、功能 20-4、返回值 …