【高级程序设计语言C++】红黑树

news2025/1/11 7:54:03

  • 1. 红黑树的概念
  • 2. 红黑树的插入
    • 2.1. 情况1
    • 2.2. 情况2
    • 2.3. 情况3
    • 2.4. 插入情况小总结
  • 3. 红黑树与AVL树的对比
  • 4. 红黑树在线生成网站

1. 红黑树的概念

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它在插入和删除操作时通过调整节点的颜色和旋转来保持树的平衡。红黑树的平衡性是通过以下规则来定义和维护的:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色。
  3. 每个叶子节点(NIL节点,空节点)都是黑色。
  4. 如果一个节点是红色的,则它的两个子节点都是黑色的。
  5. 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点(称为黑色高度)。

通过这些规则,红黑树能够保持相对平衡,从而保证了其插入、删除和查找等操作的时间复杂度都能够保持在O(log n)级别。

红黑树图:

img

关于性质的说明

  1. 不可能出现连续两个红节点

img

这种情况是不允许的!

  1. 每条路径上的黑色节点数是相同的。

img

如图中的路径A与路径B,他们黑色节点数是相同的,如果违背了这些性质,红黑树的结构将会被破坏。

2. 红黑树的插入

红黑树本身是二叉搜索树,只不过是在其基础增加了颜色的区分。所以插入是跟二叉搜索树一样的,不过要根据红黑树的规则来调整。

插入大概上分为3种情况:

2.1. 情况1

cur为红,p为红,g为黑,u存在且为红

举个简单例子,如图:

img

这种情况违背了红黑树的规则,有两个连续的红节点,此时就需要调整。

  1. 把parent和uncle变为黑色
  2. 再把grandfather变为红色
  3. 把grandfa给给cur,再继续往上看是否需要调整

调整后的红黑树:

img

根据上面的情况分析给出抽象图:

img

**假设A/B/C/D/E为一个节点,那么C/D/E的节点将会是黑色的,而A/B是红色的。**具体为什么可以参考红黑树的规则想想。

如果此时选择插入一个节点,那么将会出现情况1。

那么此时具体图如下:

img

第一次调整如图:

img

第一次调整结束后,根据调整过程cur会变动,根据情况分析,此时还需要再一次调整。

第二次调整:

img

此时白色圆圈代表的是,这棵树可能只是一颗子树,如果不是子树的的话,根节点的颜色要变黑色。

img

根据抽象图来画具象图,会有很多种情况,但最主要的情况就是cur为红,p为红,g为黑,u存在且为红.

代码如下:

img

2.2. 情况2

cur红且为p左子树,p为红,g为黑,u不存在/u存在且为黑

先举抽象图例子:

img

当A/B/C/D/E都为空的时候,那么uncle节点是不可能存在的。**因为如果uncle此时存在的话,那么就是情况1,但这里讨论的是情况2,情况2要求的是uncle节点要么存在,要么不存在,而如果uncle存在的话,将不符合红黑树结构的要求。**具象图如下:

img

那么此时就是情况2的uncle节点不存在的情况。所需要做的就是进行右单旋操作。调整后如下图:

img

为什么旋转之后,颜色是这样子变化呢?我想要cur和grandfather的颜色为黑,parent为红不行吗?别急,答案在抽象图会出来。

当A/B/C/D/E的高度为1,并且cur节点的颜色为黑色,如下图所示:

img

调整后如下图:

img

此时这种情况就是情况2,要进行旋转,旋转后如下图:

img

总体的旋转如下图所示:

img

情况2的抽象图:

img

经过旋转,但是颜色未变的视图:

img

如上图所见,这里的grandfather节点颜色是不能为黑色的,因为不符合红黑树的结构规则,因此是要将grandfather和cur的颜色变为红色,而parent颜色为黑色,如下图所示:

img

代码如下:

img

2.3. 情况3

cur红且为p右子树,p为红,g为黑,u不存在/u存在且为黑

这里只画A/B/C/D/E为一个节点时的具象图。

img

具体来说就是,当A/B/C/D/E高度为1的时候,先是碰到了情况1,调整之后,变成了情况2,再调整后,又变成了另一个方向的情况2。那么就根据这种种情况,依次旋转即可。

代码如下:

img

最后要记得把根节点的颜色变为黑色:

img

代码实现:

bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else if (data < cur->_data)
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(data);
		if (data > parent->_data)
		{
			parent->_pRight = cur;
			cur->_pParent = parent;
		}
		else if (data < parent->_data)
		{
			parent->_pLeft = cur;
			cur->_pParent = parent;
		}
		//新插入的节点默认颜色为红色,所以下面cur颜色都为红
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_pParent;
			if (parent == grandfather->_pLeft)
			{
				Node* uncle = grandfather->_pRight;
				//情况1 p为红,g为黑,u为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_pParent;
				}
				//走到这里,uncle两种情况
				//1. uncle存在且为黑
				//2. uncle不存在
				else
				{
					//情况2
					//如果cur是parent左子树,进行右旋转
					if (cur == parent->_pLeft)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况3
					else if (cur == parent->_pRight)
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else if (parent == grandfather->_pRight)
			{
				Node* uncle = grandfather->_pLeft;
				//情况1 p为红,g为黑,u为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_pParent;
				}
				//走到这里,uncle两种情况
				//1. uncle存在且为黑
				//2. uncle不存在
				else
				{
					//情况2
					//如果cur是parent左子树,进行右旋转
					if (cur == parent->_pLeft)
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					//情况3
					else if (cur == parent->_pRight)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}

2.4. 插入情况小总结

  1. 大类上分为两类,一是uncle节点为红色,而是uncle节点为黑色
  2. 根据uncle节点的颜色,又分为两种情况
  3. 如果uncle节点颜色为红色,并且parent节点和cur节点都为红色,那么就是情况1,直接变颜色即可
  4. 如果uncle节点颜色为黑色,并且parent节点和cur节点都为红色,cur是parent的左节点,那么就是情况2,单旋转
  5. 如果uncle节点颜色为黑色,并且parent节点和cur节点都为红色,cur是parent的右节点,那么就是情况3,双旋转
  6. 关键就是看uncle节点是否存在以及uncle节点的颜色

3. 红黑树与AVL树的对比

红黑树和AVL树都是常用的自平衡二叉搜索树,它们的主要目的都是为了保持树的平衡,以提高搜索、插入和删除操作的性能。然而,红黑树和AVL树在平衡的方式和性能方面存在一些差异。

  1. 平衡性:
    • 红黑树:红黑树通过在节点上引入颜色属性,并遵循一组平衡规则来保持平衡。这些规则包括节点的颜色、路径上的黑色节点数量等。红黑树的平衡性相对较弱,可以在维护平衡的同时提供较高的插入和删除性能。
    • AVL树:AVL树通过在节点上维护一个平衡因子(左子树高度减去右子树高度)来保持平衡。AVL树的平衡性相对较强,要求任何节点的平衡因子在-1、0、1之间。这种强平衡性保证了AVL树的高度始终保持在较小的范围内,但可能会导致插入和删除操作的性能稍低。
  1. 插入和删除操作:
    • 红黑树:由于红黑树的平衡性相对较弱,插入和删除操作的性能较好。红黑树在执行这些操作时只需要进行一些颜色调整和旋转操作,时间复杂度为O(log n)。
    • AVL树:由于AVL树的平衡性较强,插入和删除操作可能需要进行更多的旋转操作来保持平衡,因此性能略低于红黑树。插入和删除操作的时间复杂度为O(log n)。
  1. 查询操作:
    • 红黑树和AVL树在查询操作上的性能相似,时间复杂度为O(log n)。它们都具有快速的搜索能力,可以在平衡的树结构中进行高效的查找。
  1. 空间复杂度:
    • 红黑树和AVL树的空间复杂度都是O(n),其中n是树中节点的数量。它们在每个节点上都需要存储额外的信息来维护平衡性。

综上所述,红黑树和AVL树在平衡性和性能方面存在一些差异。选择使用哪种树结构取决于具体应用场景和需求。如果插入和删除操作频繁且对性能要求较高,可以选择红黑树。如果对平衡性要求较高且能够容忍稍低的性能,可以选择AVL树。

4. 红黑树在线生成网站

如果想验证一组数据生成的红黑树是什么样子的,可以用这个网站去看看。这个网站也是从csdn大佬的博客中发现的,这里给大家链接,希望大佬们能够有所收获。

https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

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

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

相关文章

anylabeling安装与使用说明

文章目录 一.anylabeling说明二. 安装教程1. 可执行程序方式2. python程序 一.anylabeling说明 官网:https://anylabeling.nrl.ai/docs 该工具作为一个具有Segment Anything和YOLO模型的智能标签工具&#xff0c;可以快速、准确地对图像进行标注。 二. 安装教程 1. 可执行程…

Docker Desktop 启用 Kubernetes 失败后处理

一、环境 Windows 10 C:\Users\zhuji>docker --version Docker version 24.0.2, build cb74dfc 二、问题 在setting -> Kubernetes 中&#xff0c;选中 Enable Kubernetes 后&#xff0c;长时间显示 Starting ... &#xff0c;在Images中显示几个自动下载的镜像后&…

e6zzseo:外贸独立站怎么推广

外贸独立站的推广需要一系列综合性的策略和方法&#xff0c;以吸引目标市场的访问者&#xff0c;并将他们转化为潜在客户。以下是一些推广外贸独立站的建议&#xff1a; 1. 搜索引擎优化&#xff08;SEO&#xff09;&#xff1a; e6zzseo认为优化网站可以适应搜索引擎的要求&a…

1749. 任意子数组和的绝对值的最大值

诸神缄默不语-个人CSDN博文目录 力扣刷题笔记 文章目录 1. 暴力搜索2. 动态规划3. 前缀和 1. 暴力搜索 直接用2个指针从索引0开始找到最后一个索引&#xff0c;时间复杂度大概是 O ( n 2 ) O(n^2) O(n2)吧&#xff0c;总之这么搞不行&#xff0c;以下是我用Python写的一些典型…

【2.1】Java微服务: Nacos注册中心

目录 Nacos介绍 Nacos安装 下载和安装 修改端口 启动 服务注册与发现 导入Nacos管理依赖 导入服务依赖 配置Nacos的服务地址 启动服务&#xff0c;查看已注册的服务 服务分级存储模型 分级存储模型介绍 具体结构 配置实例集群 同集群优先的负载均衡策略 服务权重配置…

vue中vuex的五个属性和基本用法,另加js-cookie的使用

VueX 是一个专门为 Vue.js 应用设计的状态管理构架&#xff0c;统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。 Vuex有五个核心概念&#xff1a; state, getters, mutations, actions, modules。 1. state&#xff1a; vuex的基本数据&…

【力扣刷题 | 第二十五天】

目录 前言&#xff1a; 474. 一和零 - 力扣&#xff08;LeetCode&#xff09; 总结: 前言&#xff1a; 今天我们依旧暴打动态规划 474. 一和零 - 力扣&#xff08;LeetCode&#xff09; 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集…

docker下载和案例

文章目录 Docker安装一,根据官方文档安装二,根据我以下方式 Docker配置错误导致漏洞一,CRLF注入漏洞介绍在nginx中该漏洞例子解决方法 目录穿越漏洞介绍解决方法 Docker安装 一,根据官方文档安装 官方文档 二,根据我以下方式 docker安装要求&#xff1a; Docker要求Ce…

Unity游戏源码分享-植物大战僵尸素材与源码

Unity游戏源码分享-植物大战僵尸素材与源码 完整版本下载地址&#xff1a; https://download.csdn.net/download/Highning0007/88191862

【Windbg】通过网络调试windows内核

环境 windows版本&#xff1a;win10_x64 1901 windbg版本&#xff1a;1.2306.12001.0 HOST 1、windbg软件设置。 点击菜单文件&#xff0c;然后如下图操作。 2、等待连接。 ************* Waiting for Debugger Extensions Gallery to Initialize **************>>&…

string模拟实现:

string模拟实现&#xff1a; 上一篇博客&#xff0c;我们对String类有了一个基本的认识&#xff0c;本篇博客我们来从0~1去模拟实现一个String类&#xff0c;当然我们实现的都是一些常用的接口。 ❓我们这里定义了一个string类型&#xff0c;然后STL标准库里面也有string&#…

【el-image图片查看时 样式穿透表格问题】

element-ui el-image图片查看 样式混乱 解决方式 ::v-deep(.el-table__cell) {position: static !important; // 解决el-image 和 el-table冲突层级冲突问题 }加个样式即可

开源数据集分类汇总(医学,卫星,分割,分类,人脸,农业,姿势等)

本文汇总了医学图像、卫星图像、语义分割、自动驾驶、图像分类、人脸、农业、打架识别等多个方向的数据集资源&#xff0c;均附有下载链接。 该文章仅用于学习记录&#xff0c;禁止商业使用&#xff01; 1.医学图像 疟疾细胞图像数据集 下载链接&#xff1a;http://suo.nz/2V…

C语言一些有趣的冷门知识

文章目录 概要1.访问数组元素的方法运行结果 2.中括号的特殊用法运行结果 3.大括号的特殊用法运行结果 4.sizeof的用法运行结果 5.渐进运算符运行结果 小结 概要 本文章只是介绍一些有趣的C语言知识&#xff0c;纯属娱乐。这里所有的演示代码我是使用的编译器是Visual Studio …

Nacos集群使用Docker部署

文章目录 一、Nacos集群部署- 单机三节点集群- 三台机器部署集群 一、Nacos集群部署 此次部署使用Docker&#xff0c;使用版本为Nacos1.4.0mysql为5.7&#xff0c;nacos的表结构sql文件&#xff1a; /** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under th…

认识 spring 中的事务 与 事务的传播机制

前言 本篇介绍spring中事务的实现方式&#xff0c;如何实现声明式事务&#xff0c;对事物进行参数的设置&#xff0c;了解事务的隔离级别和事务的传播机制&#xff1b;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录…

varint原理 - 负数的编码和解码

前一篇博客 varint原理 - 正数的编码和解码_YZF_Kevin的博客-CSDN博客我们讲了varint的实现原理&#xff0c;举例也分析对于正数的编码&#xff0c;解码过程 本篇博客&#xff0c;我们开始举例分析负数的编码和解码&#xff0c;因为负数有原码&#xff0c;反码&#xff0c;补码…

2022年03月 Python(一级)真题解析#中国电子学会#全国青少年软件编程等级考试

一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 已知a“161”&#xff0c;b“16”&#xff0c;c“8”,执行语句da>b and a>c&#xff0c;变量d的值为是&#xff1f; A&#xff1a;0 B&#xff1a;1 C&#xff1a;True D&am…

JS二维数组转化为对象

将二维数组转化为对象的形式 转之前的数据&#xff1a; 转之后&#xff1a; const entries new Map([[foo, bar],[baz, 42],[beginNode, 202212151048010054],[beginNode, 202212151048447710],]); console.log(entries)const obj Object.fromEntries(entries);console.lo…

现代C++中的从头开始深度学习:【4/8】梯度下降

一、说明 在本系列中&#xff0c;我们将学习如何仅使用普通和现代C编写必须知道的深度学习算法&#xff0c;例如卷积、反向传播、激活函数、优化器、深度神经网络等。 在这个故事中&#xff0c;我们将通过引入梯度下降算法来介绍数据中 2D 卷积核的拟合。我们将使用卷积和上一个…