红黑树的概念和模拟实现[C++]

news2024/9/27 19:22:04

文章目录

  • 红黑树的概念
    • 一、红黑树的性质
    • 红黑树原理
    • 二、红黑树的优势和比较
  • 红黑树的模拟实现
    • 构建红黑树的数据结构定义节点的基本结构和初始化方式
    • 插入新节点
      • 插入新节点的颜色
      • 调整颜色和结构以满足红黑树性质
  • 红黑树的应用场景

红黑树的概念

一、红黑树的性质

在这里插入图片描述
红黑树是一种自平衡的二叉搜索树。它的节点被标记为红色或黑色,通过特定的规则来保持树的近似平衡(最长路径不超过最短路径的二倍)。其主要规则:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的。
  3. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。[每条路径的黑色节点的数量是相等的]
  4. 如果一个节点是红色的,那么它的两个子节点都是黑色的。[也就是一条路径中,没有连续的红色节点]
  5. 每个叶子节点(NIL 节点)是黑色的。[如图中所示,就是将所有空节点当作是黑色节点,不是很重要]
    :在数路径的时候要以空节点为结束去数路径,如图有11条路经

红黑树原理

那么红黑树是如何凭借颜色来控制平衡的呢?
我们想在不违反红黑树规则,极端情况如下图【注:这种情况不一定出现,用来方便 理解】
在这里插入图片描述
最短的路径:全是黑色 最长的路径:一黑一红

那么我们设最短路径为n,最长路径也就是2n,即这张图中最长路径就是最短路径的2倍。

而还可以是这种情况如下图
在这里插入图片描述
这种情况就是最短路径的2倍大于最长路径。

所以这就是红黑树的近似平衡(即最长路径不超过最短路径的二倍
而我们这里的最长最短路径是在红黑树规则理论上而言,实际的红黑 树中最长最短不要低估是上米娜的图所示。

二、红黑树的优势和比较

红黑树之所以在众多数据结构中脱颖而出,是因为它在插入和删除操作时能够在 O(log n) 的时间复杂度内自动调整,保持平衡,从而保证了高效的查找、插入和删除性能。

以AVL树做对比:
平衡机制

  • AVL 树通过严格的平衡条件(左右子树的高度差绝对值不超过 1)来保持平衡。每次插入或删除操作后,如果导致树不平衡,需要进行复杂的旋转操作来重新平衡树。
  • 红黑树的平衡条件相对宽松,通过节点颜色的约束(红黑规则)来保持大致平衡。插入或删除操作后,调整平衡的操作相对较简单。

性能

  • 在频繁插入和删除操作的情况下,红黑树的性能通常优于 AVL 树。因为 AVL 树的平衡调整更为频繁且复杂,可能导致更多的开销。
  • 对于查找操作,AVL 树由于其更严格的平衡特性,可能会稍微快一些。

空间复杂度

  • AVL 树的每个节点需要额外存储平衡因子信息,空间复杂度相对较高。
  • 红黑树只需存储颜色信息,空间复杂度相对较低。

红黑树的模拟实现

构建红黑树的数据结构定义节点的基本结构和初始化方式

enum Color
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	pair<K, V> _kv;//值 
	RBTreeNode<K, V>* _left;//该节点的左孩子
	RBTreeNode<K, V>* _right;//该节点的右孩子
	RBTreeNode<K, V>* _parent;//该节点的父节点
	Color _col;

	RBTreeNode(const pair<K, V>& kv)
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

先定义一个枚举类型 Color 和一个模板结构体 RBTreeNode

  • 枚举类型储存红黑两种颜色。

  • pair<K, V> _kv :用于存储键值对数据。

  • RBTreeNode<K, V>* _leftRBTreeNode<K, V>* _rightRBTreeNode<K, V>* _parent :分别指向该节点的左孩子、右孩子和父节点,构建了树结构中的节点关系。

  • Color _col :使用前面定义的枚举类型来表示节点的颜色属性。

  • 结构体中的构造函数 RBTreeNode(const pair<K, V>& kv) 用于初始化节点对象,将传入的键值对赋值给 _kv,并将指针 _left_right_parent 初始化为 nullptr

插入新节点

插入新节点的颜色

首先,按照二叉搜索树的插入规则,将新节点插入到合适的位置。
新插入的节点默认是红色,原因:
如果我们插入红色节点我们破坏规则4(如果一个节点是红色的,那么它的两个子节点都是黑色的,也就是一条路径中,没有连续的红色节点)如果我们插入黑色节点我们破坏规则3(从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点)那么我们插入黑色节点的时候规则3必然会被破坏,而插入红色节点如果父节点是黑色则没有事情,如果父节点是红色才会破坏规则4==,所以相比之下插入红色节点更好。

调整颜色和结构以满足红黑树性质

  • 如果插入节点的父节点是黑色,那么无需做任何调整,树已经满足红黑树的性质。
    • 如果插入节点的父节点是红色,那么会出现违反红黑树性质的情况,需要进行调整。
      • 情况一:父节点的兄弟节点是黑色,且父节点是祖父节点的左孩子
        • 情况 1.1:插入节点是父节点的右孩子
          对父节点进行左旋,将当前情况转换为情况 1.2。
        • 情况 1.2:插入节点是父节点的左孩子
          将父节点染成黑色,祖父节点染成红色,对祖父节点进行右旋。
      • 情况二:父节点的兄弟节点是黑色,且父节点是祖父节点的右孩子
        • 情况 2.1:插入节点是父节点的左孩子
          对父节点进行右旋,将当前情况转换为情况 2.2。
        • 情况 2.2:插入节点是父节点的右孩子
          将父节点染成黑色,祖父节点染成红色,对祖父节点进行左旋。
      • 情况三:父节点的兄弟节点是红色
        将父节点和兄弟节点染成黑色,祖父节点染成红色,以祖父节点为新的插入节点继续调整。
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)
		{
			//如果u存在,cur和parent都是红,grandfather是黑
			//p,u变成黑,g变成红,g当成cur向上调整
			//u存在且是红 -> 变色进行调整
			Node* grandfather = parent->_parent;
			//p在g的左边
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle&&uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//u存在且为黑或者不存在 -> 旋转加变色
					
					if (cur == parent->_left)
					{
						//单旋
						//      g
						//   p      u
						//c
						RotateR(grandfather);

						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//双旋
						//      g
						//   p      u
						//     c
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else
			{
				//u在g的左边
				//     g
				//  u     p
				Node* uncle = grandfather->_left;
				//u存在且为红 -> 直接变色
				if (uncle&&uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//u存在且为黑或者不存在 -> 旋转加变色

					if (cur == parent->_right)
					{
						//单旋
						//      g
						//   u      p
						//             c
						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;
	}

红黑树的应用场景

红黑树在许多领域都有广泛的应用,比如:
数据库中的索引结构。
各种编程语言的标准库中,如 Java 中的 TreeMap 和 TreeSet 。
总之,红黑树作为一种高效且强大的数据结构,对于优化程序性能和提高数据管理效率具有重要意义。深入理解和掌握红黑树,将为我们在计算机科学领域的探索提供有力的支持。

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

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

相关文章

强类型语言、弱类型语言、静态类型语言、动态类型语言

强类型和弱类型&#xff0c;并没有严格绝对的界限&#xff0c;比如C/C这种典型的强类型语言&#xff0c;实际上也允许一些隐式类型传换。

一六二、记node-sass安装失败和node切换失败问题

1. 项目install失败 2. 查询问题 在stackoverflow查询到error /node_modules/node-sass: Command failed&#xff0c;明白是node版本和node-sass版本不匹配 查询自己的node版本 应该是node版本太高的问题 3. 切换node版本 使用n切换node版本失败 原因&#xff1a;而 n 默…

2-Linux系统概述

Linux系统概述 创始人——林纳斯托瓦兹&#xff0c;Linux诞生于1991年&#xff0c;作者上大学期间实现的&#xff0c;Linux的特点&#xff1a;开源、免费、拥有最为庞大的源码贡献者&#xff1b;Linux的吉祥物是企鹅 区别开源和闭源 开源&#xff1a;开放程序源代码&#xff0…

前端(五):前端工程化

前端工程化是指在企业级的前端开发项目中&#xff0c;把前端开发所需的工具、技术、流程、经验等进行规范化、标准化。 一、环境准备 &#xff08;一&#xff09;环境准备 1、Vue-cli&#xff1a;是Vue官方提供的一个脚手架&#xff0c;用于快速生成一个Vue的项目模板。 2、…

如何利用WMS仓储管理系统进行库存整合

在当今竞争激烈的市场环境中&#xff0c;库存管理已成为企业运营的核心环节之一。为了提高库存管理的效率&#xff0c;降低库存成本&#xff0c;并实现库存资源的有效整合&#xff0c;越来越多的企业开始转向采用WMS仓储管理系统解决方案这一智能化工具。本文将从库存整合的迫切…

矩阵获客时代,云微客布局SEO优化,提升企业搜索流量

矩阵这个词大家应该都不是第一次听了&#xff0c;毕竟现在互联网时代&#xff0c;想要在线上获客&#xff0c;矩阵就绕不过去的。现在&#xff0c;短视频已经成为了当下年轻人获取信息的重要途径&#xff0c;而对于商企来说&#xff0c;布局短视频矩阵不仅是线上获客、获取流量…

内网域森林之ProxyNotShell漏洞利用

点击星标&#xff0c;即时接收最新推文 本文选自《内网安全攻防&#xff1a;红队之路》 在渗透测试过程&#xff0c;如果目标环境存在Exchange服务器&#xff0c;我们也需要测试是否存在已知的远程命令执行漏洞&#xff0c;这里首先介绍ProxyNotShell。 ProxyNotShell和之前…

60 函数参数——关键参数

关键参数主要指调用函数时的参数传递方式&#xff0c;与函数定义无关。 通过关键参数可以按参数名字传递值&#xff0c;明确指定哪个值传递给哪个参数&#xff0c;实参顺序可以和形参顺序不一致&#xff0c;但不影响参数值的传递结果&#xff0c;避免了用户需要牢记参数位置和…

战锤40K极速狂飙怎么领 战锤40K极速狂飙免费喜加一入库教程

steam喜加一。游戏名称叫做《战锤40K&#xff1a;极速狂飙&#xff08;Warhammer 40,000: Speed Freeks&#xff09;》&#xff0c;这是一款背景设定在《战锤40K》宇宙中的竞速游戏&#xff0c;玩家将驾驶装载着炸裂性武器和强大能力的车辆&#xff0c;在肾上腺素爆棚的竞速比赛…

非标自动化ARM供控制器用于绕线机控制解决方案

在现代工业生产中&#xff0c;绕线机是一种广泛应用的设备&#xff0c;用于制造各种线圈和绕组。而绕线机的控制精度和速度直接影响到产品的质量和生产效率。ARMxy 嵌入式电脑作为一种高性能的控制设备&#xff0c;为绕线机控制带来了新的标准。 ARMxy 嵌入式电脑采用了先进的嵌…

计算机毕业设计选题推荐-课程教学平台-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

从零开始学python必看,最强“Python编程三剑客(pdf)”,拿来吧你!

从0开始学Python&#xff0c;就问你一句&#xff1a;慌不慌&#xff1f; 说句实在的&#xff0c;慌&#xff0c;可能是因为你自己没有完整的规划&#xff0c;其实就是不知道从何下手&#xff0c;七七八八乱学一通自然还是觉得无厘头。 但今天&#xff0c;我要跟你讲&#xff…

用户资产分级分类、命名和编码规则设计(汽车行业)

用户资产设计目的 用户资产建设要求聚合跨触点数据&#xff0c;对数据进行清洗、转换、整合&#xff0c;实现数据标准化、集成化、用户资产化&#xff0c;沉淀共性数据服务能力、以快速响应业务需求。 为支撑数据融通共享、数据分析挖掘和数据运营&#xff0c;形成数据业务化…

Python实现深度森林(Deep Forest)回归模型(deepforest回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 随着大数据和人工智能技术的发展&#xff0c;机器学习已成为解决各种复杂问题的强大工具。在众多机器学…

采用Spring Cloud +UniApp +MySql技术开发,SaaS模式的一套智慧工地云平台源码,支持多端展示:PC端、大屏端、手机端、平板端

基于微服务架构JavaSpring Cloud UniApp MySql技术开发saas模式的一套智慧工地云平台源码&#xff0c;支持多端展示&#xff1a;PC端、大屏端、手机端、平板端。 智慧工地平台支持项目级、公司级、集团级多级权限划分&#xff0c;可根据企业的组织架构进行项目权限、功能权限、…

Shopify被封?Shopify店铺开店到防封全面指南

Shopify&#xff0c;作为独立电商建站领域的佼佼者&#xff0c;其SaaS模式简化了建站流程&#xff0c;无需编程背景即可创建线上店铺&#xff0c;吸引了众多商家的目光。本文将详细讲解Shopify店铺从注册、运营到防封的每一个关键环节&#xff0c;为商家提供一站式指导&#xf…

精通推荐算法20:特征交叉之DCN -- 异构模型Wide侧引入高阶交叉

1 引言 DeepFM通过FM显式引入了二阶特征交叉&#xff0c;提升了低阶特征交叉能力。但在推荐场景中&#xff0c;三阶、四阶甚至更高阶的特征交叉同样十分重要。比如在应用市场&#xff08;如Google Play&#xff09;推荐场景中&#xff0c;“年轻”的“男性”用户&#xff0c;对…

用javaagent和javassist实现Arthas的watch功能

一、被监控的服务 spring-boot-demo 1、 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&q…

如何让AI绘画SD可控又好用?10个硬核控图能力(ControlNet)了解一下

大家好&#xff0c;我是灵魂画师向阳 Stable Diffusion&#xff08;简称SD&#xff09;作为一款目前非常流行且强大的AIGC工具&#xff0c;相信大家已经有一定了解&#xff01; 这款工具的使用说简单却也复杂&#xff0c;想要获得高质量图片&#xff0c;其硬核控图能力**&…

安装Supervisor队列进程、管理 Laravel 队列进程

在 CentOS 上安装 Supervisor 并配置 Laravel 的步骤如下&#xff1a; 1.安装 Supervisor&#xff1a; 使用以下命令安装 Supervisor&#xff1a; sudo yum install epel-release sudo yum install supervisor 2.配置 Supervisor&#xff1a; 创建一个新的 Supervisor 配置文…