C++ RBTree

news2024/11/20 7:15:50

目录

概念

性质

节点的定义

树的结构

 Insert

1.  pparent->_left == parent

1.1 uncle && uncle->_col = RED

1.2   !(uncle && uncle->_col == RED)

1.2.1  parent->_left == cur

1.2.2  parent->_right== cur

2.  pparent->_right== parent

 rotateR

rotateL

void InOrder()

bool isbalance()

总结


概念

  • 一种搜索二叉树,确保没有一条路径会大于最小路径的两倍

性质

  • 每个节点不是黑就是红
  • 根节点是黑
  • 红色节点的孩子都为黑色
  • 每个节点的往下的每条路径的黑色节点数目相同
  • 叶子节点(NIL)为nullptr节点都是黑色

节点的定义

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:
private:
	node* _root = nullptr;
};

 Insert

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

	node* cur = _root;
	node* parent = cur;
	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);
	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
	}
	else
	{
		parent->_left = cur;
	}
	cur->_parent = parent;
	//改颜色,旋转
}

操作 

  • 先插入节点
  • 默认插入的颜色为红色
  • 父亲为红色就要改变
  • 按照uncle是否为红色讨论

注意 

  • 父亲不能为空
  • 父亲对于爷爷的左右会影响旋转的方向
while (parent && parent->_col == RED)
{
	node* pparent = parent->_parent;
	if (pparent->_left == parent)
	{
		node* uncle = pparent->_right;
		if (uncle && uncle->_col == RED)
		{
			//①
		}
		else
		{
			if (parent->_left == cur)
			{
				//②
			}
			else
			{
				//③
			}
			break;
		}
	}
	else
	{
		node* uncle = pparent->_left;
		if (uncle && uncle->_col == RED)
		{
			//④
		}
		else
		{
			if (parent->_right == cur)
			{
				//⑤
			}
			else
			{
				//⑥
			}
			break;
		}
	}
	_root->_col = BLACK;
}

1.  pparent->_left == parent

1.1 uncle && uncle->_col = RED

  • 只有在uncle为红的情况下,从跟开始的路径的黑色节点数才会增加1

①处代码

parent->_col = BLACK;
uncle->_col = BLACK;
pparent->_col = RED;

cur = pparent;
parent = pparent->_parent;

 解释

  • 肯定是parent变黑,而不是新插入的节点;如果是插入的节点变黑,那么整个路径都不对了,每次插入就一定要调整,所以开始就初始化为红色;其实也不难理解,父亲是红色,cur(newnode)也是红色,但是pparent一定是黑色,如果这时候有uncle且为红色,那么把爷爷的黑色给给parent和uncle,自己变成红色;那么此时,对于爷爷的后代节点的路径黑色节点的数目不变,若pparent就是_root那么爷爷再变成黑色(放到最后处理)

1.2   !(uncle && uncle->_col == RED)

1.2.1  parent->_left == cur

旋转解释及注意事项

②处代码

rotateR(pparent);

//cur->_col = RED;
parent->_col = BLACK;
pparent->_col = RED; //这个之前一定是黑
  • 简单来说就是,旋转之后parent位置颜色变成黑色,cur 和 pparent 变成红色,pparent在旋转之前一定是黑色,因为parent是红色
1.2.2  parent->_right== cur

  • 和AVLTree的旋转一模一样

③处代码

rotateL(parent);
rotateR(pparent);

cur->_col = BLACK;//这竟然写错了
pparent->_col = RED;

2.  pparent->_right== parent

  • 2和1可以没什么区别

  else部分代码

else
{
	node* uncle = pparent->_left;
	if (uncle && uncle->_col == RED)
	{
		uncle->_col = BLACK;
		parent->_col = BLACK;
		pparent->_col = RED;
	}
	else
	{
		if (parent->_right == cur)
		{
			rotateL(pparent);

			parent->_col = BLACK;
			pparent->_col = RED;
		}
		else
		{
			rotateR(parent);
			rotateL(pparent);

			cur->_col = BLACK;
			pparent->_col = RED;
		}
		break;
	}
}

 rotateR

void rotateR(node* parent)
{
	node* subL = parent->_left;
	node* subLR = subL->_right;

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

	node* pparent = parent->_parent;

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

	if (parent == _root) //其实就是pparent为空,所以下面不用担心空指针解引用
	{
		_root = subL;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subL;
		}
		else
		{
			pparent->_right = subL;
		}
	}
	subL->_parent == pparent;
}

rotateL

void rotateL(node* parent)
{
	node* subR = parent->_right;
	node* subRL = subR->_left;

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

	node* pparent = parent->_parent;

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

	if (parent == _root)
	{
		_root = subR;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subR;
		}
		else
		{
			pparent->_right = subR;
		}
	}
	subR->_parent = pparent;
}
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}

 左右旋详解

void InOrder()

public:
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << "->" << root->_col << endl;
		_InOrder(root->_right);
	}

bool isbalance()

public:
	bool isbalance()
	{
		if (_root->_col == RED)
		{
			return false;
		}
		return _isbalance(_root, 0, 0);
	}
private:
	bool _isbalance(node* root, int numofblack, int prev)
	{
		if (root == nullptr)
		{
			if (prev == 0)
			{
				prev = numofblack;
			}
			else
			{
				if (prev != numofblack)
				{
					return false;
				}
			}
			return true;
		}

		if (root->_col == BLACK) //nullprt在上面就被刷掉了
			numofblack++;

		if (root->_parent && root->_parent->_col == RED && root->_col == RED)
		{
			return false;
		}
		//中序效率是不是有点低,但是从numofblack++来看好像只能这样
		return _isbalance(root->_left, numofblack, prev) && _isbalance(root->_right, numofblack, prev);
	}

思路

  • 检查每条路径下黑色节点数目是否一样
  • 检查相邻的是否都为红色,从cur回找parent即可

 解释

  • prev的作用是保存第一条路径的长度,且只被赋值一次

总结

  • RBTree的想法比较抽象,不然AVLTree来的易懂
  • RBTree的颜色改变比较简单,AVLTree双旋的时候_bf调节比较复杂,其时也不复杂
  • 最需重要理解的点:RBTree的5条性质,可以保证最长路径小于最短路径的两倍

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

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

相关文章

hive3从入门到精通(一)

Hive3入门至精通(基础、部署、理论、SQL、函数、运算以及性能优化)1-14章 第1章:数据仓库基础理论 1-1.数据仓库概念 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;,是一个用于存储、分析、报告的数据系统。 数据仓库的目的是构…

第十六讲:数据在内存中的存储

第十六讲&#xff1a;数据在内存中的存储 1.整数在内存中的存储1.1存储方式1.2大小端字节序1.3大小端字节序排序规则1.4为什么要有大小端1.5练习1.5.1练习11.5.2练习21.5.3练习31.5.4练习41.5.5练习51.5.6练习61.5.7练习7 2.浮点数在内存中的存储2.1练习2.2浮点数的存储2.3浮点…

常见的几种数据库通过SQL对表信息进行查询

一、前言 我们查询数据库表的信息&#xff0c;一般都使用界面化的连接工具查看&#xff0c;很少使用SQL语句去查&#xff0c;而且不同的数据库SQL语句又各自有差异。但如果通过代码去获取数据库表的信息&#xff0c;这时就需要通过SQL语句去查了&#xff0c;这个在逆向代码生成…

【案例分享】医疗布草数字化管理系统:聚通宝赋能仟溪信息科技

内容概要 本文介绍了北京聚通宝科技有限公司与河南仟溪信息科技有限公司合作开发的医疗布草数字化管理系统。该系统利用物联网技术实现了医疗布草生产过程的实时监控和数据分析&#xff0c;解决了医疗布草洗涤厂面临的诸多挑战&#xff0c;包括人工记录、生产低效率和缺乏实时…

打造专业级网页排版:全方位解析专业字体家族font-family实践与全球知名字体库导览

CSS中的字体家族&#xff08;font-family&#xff09;属性用于指定文本所使用的字体系列。它允许开发者选择一种或多种字体作为备选&#xff0c;确保在浏览器中以最佳可用字体显示文本。本文将深度解析专业级网页排版中字体家族&#xff08;font-family&#xff09;设置的实践技…

掌握Python基本语法的终极指南【基本语法部分】

一、基本语法部分 1.简单数据类型 1.1字符串类型及操作 字符串访问&#xff1a; 1.索引访问 mystr"Hello world" #索引访问 print(mystr[0]) #H print(mystr[-1]) #d print(mystr[-7]) #o print(mystr[6]) #w 2.切片访问 [头下标&#xff1a;尾下标] &#x…

车灯合面合壳密封使用UV胶的优缺点是什么呢?汽车车灯的灯罩如果破损破裂破洞了要怎么修复?

车灯合面合壳密封使用UV胶的优缺点是什么呢? 车灯合壳密封使用UV胶的优缺点如下&#xff1a; 优点&#xff1a; 快速固化&#xff1a;UV胶通过紫外线照射可以在短时间内迅速固化&#xff0c;大大缩短了车灯制造的工艺流程时间&#xff0c;提高了生产效率。高度透明&#xff…

SVG批量转为pdf超有效的方式!

最近在整理工作&#xff0c;发现ppt里面画的图智能导出svg格式无法导出pdf格式&#xff0c;由于在线的网站会把我的图片搞乱而且不想下载visio&#xff08;会把本地的word搞坏&#xff09;&#xff0c;因此琢磨出这种批量转换的方式。 1. 下载并安装Inkscape 下载链接&#xf…

基于Matlab完整版孤立词识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 孤立词识别是语音识别领域的一个重要分支&#xff0c;其目标是将输入的语音信号转换为计算机可…

成都爱尔眼科医院《中、欧国际近视手术大数据白皮书2.0》解读会圆满举行

2024年5月12日&#xff0c;爱尔眼科联合中国健康促进基金会健康传播与促进专项基金、新华社新媒体中心与中南大学爱尔眼科研究院、爱尔数字眼科研究所重磅发布《中、欧国际近视手术大数据白皮书2.0》。这是继2021、2022年在国内相继发布《国人近视手术白皮书》、《2022中、欧近…

C++笔试强训day32

目录 1.素数回文 2.活动安排 3.合唱团 1.素数回文 链接https://www.nowcoder.com/practice/d638855898fb4d22bc0ae9314fed956f?tpId290&tqId39945&ru/exam/oj 现将其转化为回文数&#xff08;这里用字符串存储比较方便转化&#xff09;&#xff0c;然后判断是否为…

无线网卡有几种接口?怎么给电脑选择一款合适的无线网卡?

前言 这篇文章一共有两个问题&#xff1a; 无线网卡有几种接口 怎么给电脑选择一款合适的无线网卡 目测这一期的文章很长很长&#xff0c;但不水。想要给笔记本或台式机升级无线网卡的小伙伴看过来了&#xff01; 最近有小伙伴问&#xff1a;华硕r555笔记本能不能升级无线…

MySql的环境配置与安装

MySQL 数据库 MySQL是一款关系型数据库 关系型数据库 ​ 基本单位是表,一个表中存储一类信息,表与表之间存在关联关系 sql语言(Structured Query Language) 数据库操作语言也属于一种编程语言,专门用作数据库操作分为三种语言 如下 sql安装使用流程 官网 href https://…

【设计模式】JAVA Design Patterns——Converter(转换器模式)

&#x1f50d;目的 转换器模式的目的是提供相应类型之间双向转换的通用方法&#xff0c;允许进行干净的实现&#xff0c;而类型之间无需相互了解。此外&#xff0c;Converter模式引入了双向集合映射&#xff0c;从而将样板代码减少到最少 &#x1f50d;解释 真实世界例子 在真实…

大众汽车集团CARIAD中国领导团队莅临知迪科技考察交流

5月23日&#xff0c;大众汽车集团旗下软件子公司CARIAD中国领导团队莅临知迪科技参观考察&#xff0c;知迪科技COO尹晓航先生率公司技术代表热情接待。 CARIAD中国一行来宾首先参观了知迪科技数采项目改制车。知迪科技软硬件工程师为考察团领导专家们讲解了知迪智驾数采系统&am…

【vs2022】安装copilot和reshaper

直接安装新版vs 17.10 自带集成的copilot支持安装resharper 可以跳过市场里的reshper安装好后依然可以直接使用vs。 resharper 2024.1.2 市场里还是i老版本&#xff1a; copilot 不兼容,这个是之前市场安装的版本 官方建议用vs intall 安装 安裝 GitHub Copilot GitHub.Co…

MySQL之Schema与数据类型优化(五)

Schema与数据类型优化 特殊类型数据 某些类型的数据并不直接与内置类型一致。低于秒级精度的时间戳就是一个例子。另外一个例子是一个IPv4地址。人们经常使用VARCHAR(15)列存储IP地址。然而&#xff0c;它们实际上是32位无符号整数。不是字符串。用小数点将地址分成四段的表示…

ThreadLocal为什么会导致内存泄漏?

问题引出&#xff1a; ThreadLocal是为了解决什么问题而产生的&#xff1f; ThreadLocal发生内存泄漏的根本原因是什么&#xff1f; 如何避免内存泄漏的发生&#xff1f;定义 为了解决多个线程同时操作程序中的同一个变量而导致的数据不一致性的问题。   假设现在有两个线程A…

ASP+ACCESS教师档案管理系统

3.1 系统功能模块图 3.2 E&#xff0d;R模型图 3.3 系统使用流程图 3.4 各个模块功能简介&#xff1a; 本系统分为五个功能模块&#xff0c;它们分别是教师信息录入模块、教师信息修改模块、教师信息查询模块、教师信息打印模块。 下面分别介绍各个模块的功能用途&#x…

社交媒体数据恢复:soma messenger

步骤1&#xff1a;检查备份文件 首先&#xff0c;我们需要确认您是否已开启Soma Messenger的自动备份功能。若已开启&#xff0c;您可以在备份文件中找到丢失的数据。 步骤2&#xff1a;清除缓存并重启应用 有时候&#xff0c;清除Soma Messenger的缓存文件可以帮助恢复丢失的…