C++——红黑树

news2024/9/21 12:44:13

目录

红黑树介绍

 红黑树实现

节点的插入 

完整代码 


 

红黑树介绍

 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。

通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

最长路径不超过最短路径的二倍

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

 红黑树实现

节点的插入 

当有新节点要插入时,我们尽量遵守规则4,因此插入新节点时,我们插入红色节点。

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

abcde是符合红黑树的子树。

如果u存在且为红,p和u变黑,g变红,继续往上处理或者g变黑

新增可以在ab的任意孩子位置

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

 p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色--p变黑,g变红

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

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2
 

完整代码 

#include<iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;

#include<time.h>
#include <assert.h>

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)
	{}
};

template<class K, class V>
struct 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* grandfater = parent->_parent;
			assert(grandfater);
			assert(grandfater->_col == BLACK);
			// 关键看叔叔
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;
				// 情况一 : uncle存在且为红,变色+继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}// 情况二+三:uncle不存在 + 存在且为黑
				else
				{
					// 情况二:右单旋+变色
					//     g 
					//   p   u
					// c
					if (cur == parent->_left)
					{
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						// 情况三:左右单旋+变色
						//     g 
						//   p   u
						//     c
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
			else // (parent == grandfater->_right)
			{
				Node* uncle = grandfater->_left;
				// 情况一
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					// 继续往上处理
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					// 情况二:左单旋+变色
					//     g 
					//   u   p
					//         c
					if (cur == parent->_right)
					{
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						// 情况三:右左单旋+变色
						//     g 
						//   u   p
						//     c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}

		}

		_root->_col = BLACK;
		return true;
	}

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

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

		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 PrevCheck(_root, 0, benchmark);
	}

private:
	bool PrevCheck(Node* root, int blackNum, int& benchmark)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			//return;
			if (benchmark == 0)//黑色节点数量的基准值
			{
				benchmark = blackNum;
				return true;
			}

			if (blackNum != benchmark)
			{
				cout << "某条黑色节点的数量不相等" << endl;
				return false;
			}
			else
			{
				return true;
			}
		}

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

		if (root->_col == RED && root->_parent->_col == RED)//如果有连续红色节点,则不是红黑树
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		return PrevCheck(root->_left, blackNum, benchmark)
			&& PrevCheck(root->_right, blackNum, benchmark);
	}

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

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

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

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

		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

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

			subR->_parent = ppNode;
		}

	}

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

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

		Node* ppNode = parent->_parent;

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

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

			subL->_parent = ppNode;
		}

	}

private:
	Node* _root = nullptr;
};
void TestRBTree1()
{
	size_t N = 1000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		cout << "Insert:" << x << ":" << i << endl;
		t1.Insert(make_pair(x, i));
	}
	cout << "IsBalance:" << t1.IsBalance() << endl;
}
int main()
{
	TestRBTree1();
	return 0;
}

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

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

相关文章

5 使用pytorch实现线性回归

文章目录前提的python的知识补充基本流程准备数据构造计算图loss以及oprimizer循环训练课程代码课程来源&#xff1a; 链接课程内容参考&#xff1a; 链接以及&#xff08;强烈推荐&#xff09;Birandaの前提的python的知识补充 pytorch 之 call, init,forward pytorch系列nn.…

Python进行因子分析

1 因子分析 1.1 定义 因子分析法(Factor Analysis)是一种利用降维的思想&#xff0c;从研究原始变量相关矩阵内部的依赖关系出发&#xff0c;把一些具有错综复杂关系的变量归结为少数几个综合因子的一种多变量统计分析方法。其优势在于不仅可以在减少大量指标分析的工作量的同…

尚硅谷hello scala-配置idea2022.1.2版本创建scala2.11.8版本maven文件

0_前置说明 软件版本 idea2022.1.2 scala2.11.8 java1.8.0_144 尚硅谷资源下载 关注b站尚硅谷 idea资源 百度网盘&#xff1a;https://pan.baidu.com/s/1Gbavx34OfF29LZqJ8dc85g?pwdyyds 提取码: yyds B站直达&#xff1a;​https://www.bilibili.com/video/BV1CK411d7a…

LabVIEW NI Linux Real-Time深层解析

LabVIEW NI Linux Real-Time深层解析NI LabVIEW Real-Time模块支持NI Linux Real-Time操作系统&#xff0c;在选定的NI硬件上提供。本文介绍了具体的新特性和高级功能&#xff0c;可让您为应用充分利用NI Linux Real-Time。Linux Shell支持NI Linux Real-Time操作系统提供了全面…

《Linux0.11源码趣读》学习笔记day6

到上次记录&#xff0c;整个操作系统的全部代码就已经从硬盘加载到内存中了&#xff0c;然后这些代码又通过jmpi跳转到0x90200处&#xff0c;即硬盘第二个扇区开始处的内容 这些内容就是第二个操作系统源代码文件setup.s 不过现在先来看一下操作系统的编译过程 操作系统的编译…

后端学习 - Docker

文章目录基本概念三个核心概念&#xff1a;镜像、容器、仓库联合文件系统 UnionFS常用命令Docker File基本概念 一次配置&#xff0c;处处使用运行在同一宿主机上的容器是相互隔离的&#xff0c;各自拥有独立的文件系统容器模型和虚拟机模型的主要区别 相较于虚拟机而言&#…

【Pytorch项目实战】之生成式网络:编码器-解码器、自编码器AE、变分自编码器VAE、生成式对抗网络GAN

文章目录生成式网络 - 生成合成图像算法一&#xff1a;编码器-解码器算法二&#xff1a;自编码器&#xff08;Auto-Encoder&#xff0c;AE&#xff09;算法三&#xff1a;变分自编码器&#xff08;Variational Auto Encoder&#xff0c;VAE&#xff09;算法四&#xff1a;生成式…

九型人格是什么?

九型人格是什么? 九型人格学(Enneagram/Ninehouse)是一个有2000多年历史的古老学问,它按照人们习惯性的思维模式,情绪反应和行为习惯等性格特质,将人的性格分为九种,又被称为九柱图,起源于中亚西亚地区,和中国的八卦图有点像,近代的九型是由六十年代智利的一位心理学…

计算机组成原理 | 第四章:存储器 | 存储器与CPU连接 | 存储器的校验 | Cache容量计算

文章目录&#x1f4da;概述&#x1f407;存储器分类&#x1f407;存储器的层次结构&#x1f955;原理&#x1f955;主存速度慢的原因&#x1f955;存储器三个主要特征的关系&#x1f955;缓存-主存层次和主存-辅存层次⭐️&#x1f4da;主存储器&#x1f407;概述&#x1f955;…

【opencv】Haar分类器及Adaboost算法人脸识别理论讲解

提到opencv,就不得不提其图像识别能力,最近旷世开源的YoloX项目兴起,作为目前Yolo系列中的最强者,本人对其也很感兴趣,但是完全没用机器学习和计算机视觉的基础,知其然,不知其所以然,于是想稍稍入坑一下opencv图像识别,了解一下相关算法,(说不定以后毕设会用到呢)。…

磨金石教育影视干货分享|朋友亲身经历—给新人剪辑师的三个建议

大学的时候有一个同学很喜欢视频剪辑。平时没事就蹲在电脑前&#xff0c;下载一些素材&#xff0c;自学剪辑软件&#xff0c;慢慢的搞一些创意剪辑。那时候自媒体短视频已经很火爆&#xff0c;这位同学剪辑的视频&#xff0c;不管质量如何就往上面发。一开始我们对于新事物的认…

Java---微服务---分布式搜索引擎elasticsearch(2)

分布式搜索引擎elasticsearch&#xff08;2&#xff09;1.DSL查询文档1.1.DSL查询分类1.2.全文检索查询1.2.1.使用场景1.2.2.基本语法1.2.3.示例1.2.4.总结1.3.精准查询1.3.1.term查询1.3.2.range查询1.3.3.总结1.4.地理坐标查询1.4.1.矩形范围查询1.4.2.附近查询1.5.复合查询1…

SpringBoot+Vue项目学生读书笔记共享平台

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

一起自学SLAM算法:10.3 机器学习与SLAM

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 前面已经分析过的8种SLAM算法案例&#xff08;Gmapping、Cartographer、LOAM、ORB-SLAM2、LSD-SLAM、SVO、RTABMAP和VINS&#xff09;都可以称为传统方法&#xff0c;因为这些算法都是在人为精心设计的特定规则下…

电子技术——MOS放大器基础

电子技术——MOS放大器基础 我们已经学过MOS可以当做一个压控流源&#xff0c;使用栅极电压 vGSv_{GS}vGS​ 控制漏极电流 iDi_DiD​ 。尽管两个量的关系不是线性的&#xff0c;稍后我们将会介绍偏置在线性区的工作方法。 构建压控压源放大器 现在&#xff0c;我们有了一个压…

【Java|golang】1664. 生成平衡数组的方案数---奇数前缀和 + 偶数前缀和

给你一个整数数组 nums 。你需要选择 恰好 一个下标&#xff08;下标从 0 开始&#xff09;并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。 比方说&#xff0c;如果 nums [6,1,7,4,1] &#xff0c;那么&#xff1a; 选择删除下标 1 &#xff0c;剩下…

[JAVA安全]JACKSON反序列化

前言 ackson是一个开源的Java序列化和反序列化工具&#xff0c;可以将Java对象序列化为XML或JSON格式的字符串&#xff0c;以及将XML或JSON格式的字符串反序列化为Java对象。 依赖 <dependency><groupId>com.fasterxml.jackson.core</groupId><artifact…

中国省际铁路通行时间数据

一、数据简介 本数据来自南京大学长江产业经济研究院《全国统一大市场下的省际铁路交通研究报告》的附录部分。中国的铁路&#xff08;高铁&#xff09;建设取得了辉煌成果。但受铁路时刻众多、历史数据不容易搜集整理的限制&#xff0c;学术与政策研究者一直无法对铁路建设的时…

三、JDBC详解

教程相关资料&#xff1a;https://www.aliyundrive.com/s/wMiqbd4Zws6 1&#xff0c;JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 JDBC概念 JDBC 就是使用Java语言操作关系型数据库的一套…

Nacos 配置中心 服务端推送变更源码讲解

目录 1. 配置引起变更的两种方式 1.1 后台管理直接操作 1.2 NacosClient 调用 RPC 接口 2. 变更事件处理 AsyncNotifyService 2.1 HTTP 任务 2.2 RPC任务 2.3 NacosServer 其他节点接收到消息后如何处理 3. 客户端推送实现&#xff1a;DumpService.dump 接着上一篇 Nac…