【C++】红黑树

news2024/11/19 20:27:03

一.红黑树的概念与性质

1.概念

红黑树是二叉搜索数的一种, 相比于AVL树(二叉平衡搜索树)红黑树通过减少旋转的次数来进一步优化了查找效率, 在每个节点上增加一个存储位表示节点的颜色, Red or Black, 通过对任何一条从根到叶子的路径上各个节点着色方式的限制, 红黑树确保没有一条路径会比其他路经长超出两倍, 故是接近平衡的, 红黑树通过控制五条规则来确保它的近平衡性, 从而既不频繁旋转, 又保证效率在logN级别

2.性质

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

2).根节点总是黑色

3).每条路径的NIL节点都是黑色的

4).每条路径的黑色节点数量必须相同

5).每条路径不能用两个连续的红色节点

二.红黑树insert模拟实现

1.为何插入的新节点都是红色的

插入新节点为红色, 然后再根据规则向上调整, 只调整当前分支即可, 如果插入新节点为黑色, 根据红黑树的要求每条路径黑色节点都相同, 则需要调整所有路径, 故为了便于实现, 默认插入的新节点都为红色

2.红黑树插入节点的三种情况

父亲为黑不需要处理

情况一: 父节点为红, 叔叔节点为红

情况二: 父节点为红, 叔叔节点为空/黑, 新插入节点一边倒, 单旋

情况三: 父节点为红, 叔叔节点为空/黑, 新插入节点在中间, 双旋

3.代码

#define _CRT_SECURE_NO_WARNINGS 1

enum Color
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;
	pair<K, V> _kv;
	//构造
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_col(RED)//默认添加的新节点为红色
		,_kv(kv)
	{}
};

template<class K, class V>
class RBTree
{
public:
	typedef RBTreeNode<K, V> Node;
	bool insert(const pair<K, V>& val)
	{
		if (root == nullptr)
		{
			//如果此时为空树
			root = new Node(val);
			//将根节点修正为黑色
			root->_col = BLACK;
			return true;
		}
		Node* cur = root;
		Node* parent = nullptr;//cur的父节点
		while (cur)
		{
			if (cur->_kv.first > val.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < val.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else//(cur->_kv.first == val.first)
			{
				//如果插入的节点是重复值, 则插入失败
				return false;
			}
		}
		cur = new Node(val);
		if (parent->_kv.first > cur->_kv.first)
		{
			parent->_left = cur;
		}
		else if (parent->_kv.first < cur->_kv.first)
		{
			parent->_right = cur;
		}
		cur->_parent = parent;
		//以上为插入节点
		//-------------------------------------------------------
		//以下为调整为红黑树
		//因为默认插入的节点为红色,所以如果出现了两个连续为红的节点就需要处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			Node* uncle = nullptr;
			//确定叔叔节点的位置
			if (grandfather->_left == parent)
			{
				uncle = grandfather->_right;
			}
			else//grandfather->_right == parent
			{
				uncle = grandfather->_left;
			}
			//将分为三种情况
			//1.父节点为红,叔叔节点存在且为红(变色 + 向上迭代)
			//2/3.父节点为红,叔叔节点不存在或者存在且为黑(旋转 + 变色)
			if (uncle && uncle->_col == RED)//情况一
			{
				//父变黑,叔叔变黑,祖父变红->向上迭代
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->_parent;
			}
			else//情况二/三
			{
				//情况二
				//     g
				//   p   u
				// c
				if (uncle == grandfather->_right && cur == parent->_left)
				{
					//右单旋
					RotateR(grandfather);
					//
					parent->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
				//     g
				//   u   p
				//         c 
				else if (uncle == grandfather->_left && cur == parent->_right)
				{
					//左单旋
					RotateL(grandfather);
					//
					parent->_col = BLACK;
					grandfather->_col = RED;
					break;

				}
				//情况三
				//     g
				//   u   p
				//     c
				else if (uncle == grandfather->_left && cur == parent->_left)
				{
					//左双旋
					RotateRL(grandfather);
					//
					grandfather->_col = RED;
					cur->_col = BLACK;
					break;

				}
				//     g
				//   p   u
				//     c
				else if (uncle == grandfather->_right && cur == parent->_right)
				{
					//右双旋
					RotateLR(grandfather);
					//
					grandfather->_col = RED;
					cur->_col = BLACK;
					break;

				}
				else
				{
					cout << "不存在这种情况" << endl;
					exit(-1);
				}
			}
		}
		root->_col = BLACK;
		return true;
	}

	void inorder()
	{
		_inorder(root);
	}
    //检查是否为红黑树
	bool isRBTree()
	{
		if (root->_col == RED)
		{
			cout << "出错: 根节点为红" << endl;
			return false;
		}
		//判断是否有连续红节点,且每条路径的黑节点是否相等
		int benchmark = 0;//算出最左路径的黑节点个数
		Node* cur = root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++benchmark;
			}
			cur = cur->_left;
		}
		return _isRBTree(root, 0, benchmark);
	}

private:
	//四种旋转
	void RotateL(Node* prev)
	{
		Node* subR = prev->_right;
		Node* subRL = subR->_left;
		Node* ppNode = prev->_parent;

		prev->_right = subRL;
		if (subRL)
		{
			subRL->_parent = prev;
		}

		subR->_left = prev;
		prev->_parent = subR;

		if (root == prev)
		{
			root = subR;
		}
		else
		{
			if (ppNode->_left == prev)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
		}
		subR->_parent = ppNode;
	}

	void RotateR(Node* prev)
	{
		Node* subL = prev->_left;
		Node* subLR = subL->_right;
		Node* ppNode = prev->_parent;

		subL->_right = prev;
		prev->_parent = subL;

		prev->_left = subLR;
		if (subLR)
		{
			subLR->_parent = prev;
		}

		if (root == prev)
		{
			root = subL;
		}
		else
		{
			if (ppNode->_left == prev)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
		}
		subL->_parent = ppNode;
	}

	void RotateRL(Node* prev)
	{
		//先右旋, 再左旋
		RotateR(prev->_right);
		RotateL(prev);
	}

	void RotateLR(Node* prev)
	{
		//先左旋, 再右旋
		RotateL(prev->_left);
		RotateR(prev);
	}

	void _inorder(Node* root)
	{
		if (root)
		{
			_inorder(root->_left);
			cout << root->_kv.first << "--" << root->_kv.second << endl;
			_inorder(root->_right);
		}
	}
    //检查是否为红黑树
	bool _isRBTree(Node* root, int blackNum, int benchmark)
	{
		if (root == nullptr)//走到空节点
		{
			if (benchmark == blackNum)
			{
				//for debug
				//cout << blackNum << endl;
				return true;
			}
			else
			{
				//for debug
				//cout << blackNum << endl;
				cout << "不是所有路径的黑色节点个数都相同" << endl;
				return false;
			}
		}
		if (root->_col == BLACK)
		{
			++blackNum;
		}
		//判断是否有连续的红节点
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "出现了连续的红色节点" << endl;
			return false;
		}
		return _isRBTree(root->_left, blackNum, benchmark)
			&& _isRBTree(root->_right, blackNum, benchmark);
	}

	Node* root = nullptr;
};

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

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

相关文章

亚马逊云科技re:Invent:创新永无止境,科技重塑未来

作为全球IT行业一年一度的科技盛宴&#xff0c;2022亚马逊云科技re:Invent全球大会吸引了来自世界各国的开发者与企业客户的高度关注&#xff0c;其中自然也包括了广大中国企业。作为全球云计算领域的风向标&#xff0c;本届大会究竟揭示了怎样的行业趋势&#xff1f;发布了哪些…

【超聚变】FusionServer1288H V6安装CentOS系统

【超聚变】FusionServer1288H V6安装CentOS系统1、创建Raid2、安装系统2.1.使用IBMC地址&#xff0c;登录web界面2.2.选择启动虚拟控制台2.3.挂载镜像2.4.选择启动盘2.5.安装系统2.6.选择英文安装2.7.时区选择上海&#xff08;Asia/Shanghai&#xff09;2.8.选择最小化安装&…

【Vue源码解析】mustache模板引擎

模板引擎什么是模板引擎实现 Scanner 类根据模板字符串生成 tokens在 index.js 引入 parseTemplateToTokens实现 tokens 的嵌套One More Thingtokens 结合数据解析为 dom 字符串定义 lookup 函数定义 renderTemplate 函数什么是模板引擎 模板引擎是将数据变为视图最优雅的解决…

如何清除chrome浏览器缓存

清除浏览器的缓存知识调用前言引入具体操作知识调用 文章中可能用到的知识点前端学习&#xff1a;浏览器缓存方式有哪些&#xff08;http协议 websql indexDB cookie localstorage sessionstorage&#xff09;如何查看Chrome浏览器的页面缓存内容【详细教程】 前言引入 上期文…

微服务框架 SpringCloud微服务架构 微服务保护 31 限流规则 31.1 簇点链路

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护31 限流规则31.1 簇点链路31.1.1 簇点链路31.1.2 快速入门31 限流规则 31.1 簇点链路 31.1.1 簇…

52、网络

目录 一、网络通信 二、网络 三、 ip地址 四、域名 五、端口号 六、网络通信协议 1、协议&#xff08;tcp/ip&#xff09; 2、TCP和UDP 七、InetAddress类 八、Socket 1、基本介绍&#xff1a; 2、TCP网络通信编程 3、UDP网络通信编程&#xff08;了解即可&#xff0c…

完整版JAVA物业管理系统源码带小程序+文字安装教程+视频

这套系统还包含了小程序前端源码。 技术架构 技术框架&#xff1a;springboot ssm shiro layui 运行环境&#xff1a;IntelliJ IDEA 2022 jdk1.8 Mysql5.7.4 maven nginx 宝塔面板 文字安装教程 1.下载源码后打开小皮面板&#xff0c;安装mysql5.7数据库&#xff0c;创…

【JavaEE】计算机是怎样工作的,五分钟带你理解计算机!!!

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀学瓜的主页&#x1f319; 专栏&#xff1a;JavaEE 分享&#xff1a;一、让你知道我的存在&#xff1b;二、让你存在下去&#xff0c;对我来说都是危险的&#xff0c;都违反第一条公理。 ——《三体》 主要内容&#xff1a;…

C++11标准模板(STL)- 算法(std::push_heap)

定义于头文件 <algorithm> 算法库提供大量用途的函数&#xff08;例如查找、排序、计数、操作&#xff09;&#xff0c;它们在元素范围上操作。注意范围定义为 [first, last) &#xff0c;其中 last 指代要查询或修改的最后元素的后一个元素。 数据结构的堆物理结构是数…

Linux离线安装RabbitMQ

使用docker离线安装rabbitmq 1.在外网环境下载rabbitmq:management镜像 命令&#xff1a;docker pull rabbitmq:management 启动容器&#xff08;即验证镜像是否有用&#xff09;&#xff1a; 命令&#xff1a;docker run -d --hostname wxrabbit --name rabbitmq -p 15672:15…

【深度学习】学习率预热和学习率衰减 (learning rate warmup decay)

背景 在深度学习中学习率这个超参数&#xff0c;在选取和调整都是有一定策略的&#xff0c;俗称炼丹。有时我们遇到 loss 变成 NaN 的情况大多数是由于学习率选择不当引起的。 神经网络在刚开始训练的时候模型的权重(weights)是随机初始化的&#xff0c;选择一个较大的学习率…

干货 | 浅谈携程大住宿研发效能提升实践

作者简介Mia &#xff0c;携程高级项目经理&#xff0c;负责酒店Devops实践&#xff0c;关注Devops/敏捷等领域。YY&#xff0c;携程敏捷教练&#xff0c;负责团队敏捷转型&#xff0c;研发效能提升实践&#xff0c;关注Agile、Devops、研发效能等领域。一、前言管理大师彼得德…

[ChatGPT为你支招]如何提高博客的质量,找到写作方向,保持动力,增加粉丝数?

0. 引言 作为一个博主&#xff0c;您可能会面临很多挑战&#xff0c;比如如何提高博客的质量&#xff0c;如何找到自己的写作方向&#xff0c;如何保持持续写作的动力&#xff0c;以及如何增加博客的粉丝数量。在这篇文章中&#xff0c;我们将为您提供一些有用的建议&#xff…

Maven打包报错:找不到符号,类BASE64Encoder,程序包sun.misc

背景 一个基于若依单体架构的多模块 Maven 项目的国产化迁移适配&#xff0c;由于是客户的代码&#xff0c;我们不用关心具体的功能实现&#xff0c;直接来做迁移即可。实施时&#xff0c;按照我们总结的整改建议调整源码&#xff0c;具体迁移适配过程可参考本专栏的其他文章。…

ADI Blackfin DSP处理器-BF533的开发详解19:LAN的网口设计

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 功能介绍 BF533说实话用来做LAN的应用有些许勉强&#xff0c;因为他自己不带网口&#xff0c;要做的话&#xff0c;需要在总线上挂&#xff0c;那…

彻底搞懂ESLint与Prettier在vscode中的代码自动格式化

前言 前端代码格式化社区提供了两种比较常用的工具ESLint和Prettier&#xff0c;他们分别提供了对应的vscode的插件&#xff0c;二者在代码格式化方面有重叠的部分&#xff0c;规则不一致时会导致冲突。vscode作为前端开发编辑器已经越来越普遍了&#xff0c;这需要开发者在vs…

ChatGPT与搜索引擎合体,谷歌都不香了,LeCun转发|在线可玩

Alex Pine 发自 凹非寺量子位 | 公众号 QbitAI见惯了列表式搜索引擎&#xff0c;你有没有想过给它换种画风&#xff1f;有人脑洞大开&#xff0c;把艳惊四座的ChatGPT和必应搜索结合起来&#xff0c;搞出了一个智能搜索引擎&#xff1a;既有ChatGPT式的问答&#xff0c;又像普通…

VS——路径说明

$(TargetDir)输出目标的路径 $(TargetPath) 输出文件.exe的路径 $(TargetName) 项目名字 $(TargetFileName) 输出的.exe的名字 $(TargetExt) 文件的扩展名 $(ProjectDir)工程目录 目录根据下面的文件来的 $(IntDir)获取中间文件 $(OutDir)文件输出路径 $(Solu…

神马操作Kafka 竟宣布弃用 Java 8

Kafka 3.0.0 发布了&#xff1a; ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 主要更新如下&#xff1a; The deprecation of support for Java 8 and Scala 2.12 Kafka Raft support for snapshots of the metadata topic and ot…

ADI Blackfin DSP处理器-BF533的开发详解15:RS232串口的实现(含源代码)

硬件准备 ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 实现原理 ADSP-EDU-BF533 开发板上设计了一个 RS232 接口&#xff0c;该接口通过 ADSP-BF53x 上的 UART 接口&#xff0c;扩展 RS232协议的芯片实…