【数据结构】:红黑树

news2025/1/10 16:48:45

1、红黑树的简介

红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构。

红黑树是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。 

红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 

它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目

2、红黑树的优点

  • 红黑树保证了一种弱平衡,即树的高度最多是2倍的对数级别。这使得红黑树在插入和删除操作时具有更高的灵活性
  • AVL树是一种严格的平衡树,保证任何节点的左子树和右子树的高度差(平衡因子)不超过1。这确保了AVL树在平衡方面表现更好,但在插入和删除操作时可能需要更多的旋转来维持平衡
  • 红黑树是具备了某些特性的二叉搜索树,能解决非平衡树问题,红黑树是一种接近平衡的二叉树(说它是接近平衡因为它并没有像AVL树的平衡因子的概念,它只是靠着满足红黑节点的5条性质来维持一种接近平衡的结构,进而提升整体的性能,并没有严格的卡定某个平衡因子来维持绝对平衡)

3、红黑树的基本概念和性质

3.1、红黑树的基本定义 

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

 3.2、红黑树性质的要点

  • 根节点是黑色的
  • 不能有两个相连的红色节点。这意味着从任意节点到其子节点的路径上不能出现连续的红色节点,以避免出现不平衡情况
  • 从任意节点出发,到达其每个叶子节点的路径上的黑色节点数量必须相同。这确保了树的高度始终保持在一个合理的范围内,从而保证了高效的查找操作
  • 空节点(NIL节点)被认为是黑色的。这样可以确保每个路径上的黑色节点数量相等,即使是经过了空节点的路径。
  • 性质3和性质4我们可以推出一个红黑树中最长路径应该是最短路径的二倍,最短路径:全为黑,最长路径:一黑一红
     

我们需要注意的是空节点(NIL节点)被认为是黑色的,从任意节点出发,到达其每个叶子节点的路径上的黑色节点数量必须相同

 4、红黑树的效率

4.1 红黑树效率


红黑树的查找,插入和删除操作,时间复杂度都是O(logN)

4.2 红黑树和AVL树的比较

  • AVL树的时间复杂度虽然优于红黑树,但是对于现在的计算机,cpu太快,可以忽略性能差异
  • 红黑树的插入删除比AVL树更便于控制操作
  • 红黑树整体性能略优于AVL树,红黑树旋转情况少于AVL树

5、对旋转的基本理解 

在数据结构中,旋转是一种常见的操作,用于调整树或其他数据结构的结构以保持平衡或满足某些性质。在红黑树、AVL树、二叉搜索树等数据结构中,旋转操作通常用于平衡树的结构,以确保高效的插入、删除和查找操作。旋转操作有两种基本类型:左旋和右旋

5.1、左旋

左旋是一种将某个节点的右子节点旋转上来的操作。它会将当前节点下移,并且将其右子节点提升为新的父节点。这可以用于解决树向右倾斜的情况,以保持树的平衡。左旋的基本步骤:

  • parent:30
  • subR:50
  • subRL:b
  1. subRL变成parent的右子树 
  2. parent变成subR的左子树
  3. subR变成新根

 5.2、右旋

  • 50为parent,30为subL,b为subLR 
  1. subLR变成parent的左子树 
  2. parent变成subL的右子树
  3. subL变成新根

5.3、代码展示




template<class K, class V>
void AVLTree<K, V>::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;
	}
 
}

template<class K, class V>
void AVLTree<K, V>::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)   //parent就是根,无需向上调整
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else                  //parent不是根,继续向上调整
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;
		}
		else
		{
			parentParent->_right = subL;
		}
 
		subL->_parent = parentParent;
	}
 
}

 6、红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
  1. 按照二叉搜索的树规则插入新节点
  2. 检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何
性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连
在一起的红色节点,此时需要对红黑树分情况来讨论:
约定 :cur 为当前节点, p 为父节点, g 为祖父节点, u 为叔叔节点
  • 情况一: cur为红,p为红,g为黑,u存在且为红
  • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
  • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑

6.1、情况一

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

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

  1. 如果g是根节点,调整完后要将g变为黑色
  2. 如果g不是根节点,继续向上调整

6.2、情况二

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

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

6.3、情况三

情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
解决方式:p为 g 的左孩子, cur p 的右孩子,则针对 p做左单旋转,再对g进行右单旋转;相反,
                  p为g 的右孩子, cur p 的左孩子,则针对 p做右单旋转,再对g进行左单旋转

6.4、插入代码展示

template<class K, class V>
bool RBTree<K,V>::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;
}

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

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

相关文章

Leetcode—2471.逐层排序二叉树所需的最少操作数目【中等】(置换环解法!)

2023每日刷题&#xff08;二十七&#xff09; Leetcode—2471.逐层排序二叉树所需的最少操作数目 置换环解题思想 参考自网络 总交换次数 每一层最小交换次数之和 每一层元素个数 - 置换环数 实现代码 /*** Definition for a binary tree node.* struct TreeNode {* …

YOLO目标检测——水果检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;水果分类检测数据集的应用场景主要包括农贸市场监管、水果品质检测、超市零售管理等数据集说明&#xff1a;水果分类检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含有苹果香蕉橙子图片标签说明&#xff1a;使…

功能案例 -- 通过开关,改变白天和黑夜

效果展示 代码展示 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>:root {--default-bac-color: #f…

MGA-WPA

作者未提供代码

分布式理论基础:CAP定理

什么是CAP CAP原则又称CAP定理&#xff0c;指的是在一个分布式系统中&#xff0c;Consistency&#xff08;一致性&#xff09;、 Availability&#xff08;可用性&#xff09;、Partition tolerance&#xff08;分区容错性&#xff09;这三个基本需求&#xff0c;最多只能同时…

关于我用半个月过了软件设计师这件事

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 前言 有关考取软件设计师证书的好处我就不在…

hive和spark-sql中 日期和时间相关函数 测试对比

测试版本&#xff1a; hive 2.3.4 spark 3.1.1 hadoop 2.7.7 1、增加月份 add_months(timestamp date, int months)add_months(timestamp date, bigint months)Return type: timestampusage:add_months(now(),1) 2、增加日期 adddate(timestamp startdate, int days)…

【信息安全原理】——传输层安全(学习笔记)

&#x1f4d6; 前言&#xff1a;为保证网络应用&#xff0c;特别是应用广泛的Web应用数据传输的安全性&#xff08;机密性、完整性和真实性&#xff09;&#xff0c;可以在多个网络层次上采取安全措施。本篇主要介绍传输层提供应用数据安全传输服务的协议&#xff0c;包括&…

Linux内核有什么之内存管理子系统有什么第七回 —— 小内存分配(5)

接前一篇文章&#xff1a;Linux内核有什么之内存管理子系统有什么第六回 —— 小内存分配&#xff08;4&#xff09; 本文内容参考&#xff1a; linux进程虚拟地址空间 《趣谈Linux操作系统 核心原理篇&#xff1a;第四部分 内存管理—— 刘超》 4.6 深入理解 Linux 虚拟内存…

服务器中了locked勒索病毒怎么处理,locked勒索病毒解密,数据恢复

近几年&#xff0c;网络应用技术得到了迅速发展&#xff0c;越来越多的企业开始走向数字化办公&#xff0c;极大地为企业的生产运营提供了帮助&#xff0c;但是网络技术的发展也为网络安全埋下隐患。最近&#xff0c;locked勒索病毒非常嚣张&#xff0c;几乎是每隔两个月就会对…

MySQL 约束特殊查询

MySQL 约束&特殊查询 文章目录 MySQL 约束&特殊查询1. 数据库约束1.1 约束类型1.2 NULL约束1.3 NUIQUE&#xff1a;唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束1.6 FOREIGN KEY&#xff1a;外键约束1.7 CHECK约束 2. 表的关系2.1 一…

EXCEL中将UTC时间戳转为日期格式(精确到秒)

UTC时间戳的格式通常是一个整数&#xff0c;表示从1970年1月1日00:00:00 UTC到当前时间的总秒数。它可以以秒或毫秒为单位表示。例如&#xff0c;如果当前时间是2023年3月17日 12:34:56 UTC&#xff0c;则对应的UTC时间戳为1679839496&#xff08;以秒为单位&#xff09;或1679…

【Spring】事务实现原理

在使用事务的时候需要添加EnableTransactionManagement注解来开启事务&#xff0c;Spring事务底层是通过AOP来实现的&#xff0c;所以启用事务后&#xff0c;同样会向容器中注入一个代理对象创建器&#xff0c;AOP使用的是AnnotationAwareAspectJAutoProxyCreator&#xff0c;事…

【阿里云】函数计算 X 通义千问快速部署

写在前面&#xff1a;博主是一只经过实战开发历练后投身培训事业的“小山猪”&#xff0c;昵称取自动画片《狮子王》中的“彭彭”&#xff0c;总是以乐观、积极的心态对待周边的事物。本人的技术路线从Java全栈工程师一路奔向大数据开发、数据挖掘领域&#xff0c;如今终有小成…

多孔对跨孔电磁波CT联合反演

多孔对跨孔电磁波CT联合反演 前言 针对单一孔对跨孔电磁波CT反演数据拼接剖面不连续&#xff0c;相邻钻孔间吸收系数差异大的问题&#xff0c;采用多孔对跨孔电磁波CT联合反演。 1、多孔对数据拼接 将所有单一剖面连接为多孔剖面&#xff0c;以‘东大北大’的原则编号。 …

CorelDRAW2023中文免费版矢量图设计软件

设计工作经验丰富的人一定对比过多种设计软件&#xff0c;在对众多矢量图设计软件进行对比之后&#xff0c;多数资深设计师认为CorelDRAW的专业性、便捷性以及兼容性的综合表现更好&#xff0c;而且软件还配置了海量艺术笔&#xff0c;这让工作成果更为出众&#xff0c;因此更愿…

linux安装并配置git连接github

git安装 sudo apt-get install git git信息配置 git config --global uer.name "yourname" git config --global user.email "youremail" 其中&#xff0c;yourname是你在github上配置的用户名&#xff0c;youremail是你在github上设置的邮箱 查看git…

如何让useEffet支持async/await

前言 刚开始学react写过类似下面的代码&#xff0c;就是想直接在useEffect中使用async/await。然后浏览器就会报错如下图&#xff1a; useEffect(async () > {const res await Promise.resolve({ code: 200, mes: });}, [])报错的意思&#xff1a; useEffect 期望接受一…

FM模型与POLY2模型

两个核心细节 掌握FM&#xff0c;有两个细节需要注意&#xff1a;参数量级的变化和时间复杂度的变化。 首先对于参数量级&#xff0c;由线性模型到多项式模型到FM模型参数量级变化为&#xff1a; n–>n*n–>kn (k<<n) 其次是由原始FM公式到化简之后的FM公式复杂…

ArduPilot开源代码之Aocoda-RC F405V2 Mark4 7“ PNP适配

ArduPilot开源代码之Aocoda-RC F405V2 Mark4 10"适配 1. 源由2. 安装2.1 Installing Ground Station (GCS) software2.2 Autopilot System Assembly Instructions2.3 Loading Firmware2.4 Connect Mission Planner to AutoPilot 3. 配置3.1 Basic System Overview3.2 Fram…