【C++进阶06】红黑树图文详解及C++模拟实现红黑树

在这里插入图片描述

一、红黑树的概念及性质

1.1 红黑树的概念

AVL树用平衡因子让树达到高度平衡
红黑树可以认为是AVL树的改良
通过给每个节点标记颜色让树接近平衡
以减少树在插入节点的旋转
在每个结点新增一个存储位表示结点颜色
可以是Red或Black
通过对任何一条从根到叶子的路径上
各个结点着色方式的限制
红黑树确保没有一条路径会比其他路径长出
俩倍,因而是接近平衡的

1.2 红黑树的性质

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

在这里插入图片描述

为啥满足上面性质的红黑树就能保证
其最长路径节点个数不会超过最短路径
节点个数的两倍?

由性质3可得出不能出现连续红色节点
由性质4可得出每条路径有相同黑色节点数量

极限情况下
最短路径:全黑
最长路径:一黑一红

由此可得出
最长路径不会超过最短路径的两倍
在这里插入图片描述

1.3 为什么更常用红黑树而不是AVL树?

AVL树: 是一颗高度平衡的二叉树
查找效率: O ( l o g N ) O(logN) O(logN)
但是这样的效率是在插入元素时
经常性的旋转换来的

红黑树: 是一颗接近平衡的二叉树
假设全部黑节点有N个
整棵树的节点数量:[N, 2N]之间
最短路径长度: O ( l o g N ) O(logN) O(logN)
最长路径长度: O ( 2 l o g N ) O(2logN) O(2logN)
查找效率: O ( 2 l o g N ) O(2logN) O(2logN)

10亿数据AVL树最多查找30次
红黑树最多也就查找60次
对于cpu的运行速度来说几乎可以忽略不计
但红黑树的规则相对于AVL树没那么严格
在插入元素时,不会经常旋转
所以综合而言红黑树更胜一筹

如图: 对于AVL树必定旋转
红黑树则不用
在这里插入图片描述

二、红黑树模拟实现的基本框架

2.1 红黑树节点的定义

跟AVL树一样
只是AVL树采用平衡因子
让树达到平衡
而红黑树对节点进行颜色标记
让树达到平衡
定义一个枚举表示节点颜色

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

2.2 红黑树的插入

还是和AVL树一样

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			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);
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}

		// new的节点的parent还指向空
		cur->_parent = parent;

		// 插入黑色节点还是红色节点?

		return true;
	}

插入走到这里如果是AVL树
此时需要更新平衡因子

红黑树采用的是标记节点颜色
让树达到平衡
需要考虑的是插入什么颜色的节点?

  1. 插入黑色节点
    会违反规则4,影响到每条路径
  2. 插入红色节点
    如果插入节点的父节点也是红色节点
    则会违反规则3影响当前局部节点

很明显插入红色节点更划算
所有插入的节点都默认是红色
如果违反红黑树的规则,再进行调整

三、对插入节点调整的解析

如果插入节点的父节点为黑
则无需处理
如果为红,则分为三种情况

情况一:

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

cur为当前节点,p为父节点
g为祖父节点,u为叔叔节点
在这里插入图片描述
把p和u变黑,g变红
在这里插入图片描述
如果grandfather的parent也为红
把grandfather改为cur
继续按刚才的步骤往后迭代
在这里插入图片描述
如果grandfather为根节点
把grandfather改为黑色
颜色调整结束
在这里插入图片描述

情况二:

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

此树可能是完整树也可能是子树
u节点不存在
在这里插入图片描述
p为g的左孩子,cur为p的左孩子
则进行右单旋转
相反
p为g的右孩子,cur为p的右孩子
则进行左单旋转
p、g变色–p变黑,g变红
在这里插入图片描述

在这里插入图片描述
下图则是u节点存在的情况
在这里插入图片描述
c为下面4种情况的
任意一种包含一个黑节点的红黑树
d和e可能是空或者一个红节点
在这里插入图片描述
插入新节点,更新完后
继续往后更新
就是情况二的u存在的情况
在这里插入图片描述

情况三:

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

跟情况二完全类似
只是情况三为双旋
情况二是单旋

p为g的左孩子,cur为p的右孩子
则针对p做左单旋转
相反
p为g的右孩子,cur为p的左孩子
则针对p做右单旋转

则转换成了情况2
此图为u不存在
u存在参考情况二
在这里插入图片描述

四、红黑树插入代码的全部实现

详解都在代码注释
各位友友们请耐心看完

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);
	if (parent->_kv.first > kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}

	// new的节点的parent还指向空
	cur->_parent = parent;

	// 如果新插入节点破坏了红黑树规则
	// 则更新节点颜色
	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (grandfather->_left == parent)
		{
			Node* uncle = grandfather->_right;
			// 情况1:u存在且为红,变色处理,并继续往上处理
			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上调整
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 情况2+3:u不存在或者u存在且为黑,旋转+处理
			{
				// 如果插入节点在父节点的左,c、p、g呈一条斜线
				//     g
				//   p   u
				// c
				if (parent->_left == cur)
				{
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED; 
				}
				else
				{
					// 插入节点在父节点的右,c、p、g呈一条折线
					//      g
					//   p      u
					//     c
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = BLACK;
					// parent->_col = RED; // 父亲本就是红,变一下双重保险
					grandfather->_col = RED;
				}

				break;
			}
		}
		else // (grandfather->_right == parent)
		{
			Node* uncle = grandfather->_left;
			// 情况1:u存在且为红,变色处理,并继续往上处理
			if (uncle && uncle->_col == RED)
			{
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;

				// 继续往上调整
				cur = grandfather;
				parent = cur->_parent;
			}
			else // 情况2+3:u不存在或者u存在且为黑,旋转+处理
			{
				//   g
				// u    p
				//         c
				if (parent->_right == cur)
				{
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else
				{
					//   g
					// u     p
					//     c
					RotateR(parent);
					RotateL(grandfather);
					cur->_col = BLACK;
					grandfather->_col = RED;
				}
				break;
			}
		}
	}

	_root->_col = BLACK; // 做个双保险,无论那种情况把根都变成黑的
	return true;
}

五、红黑树全部代码模拟实现

gitee链接

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语言中的函数和方法

函数定义 1.1.1. golang函数特点&#xff1a; • 无需声明原型。• 支持不定 变参。• 支持多返回值。• 支持命名返回参数。 • 支持匿名函数和闭包。• 函数也是一种类型&#xff0c;一个函数可以赋值给变量。• 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。• 不…

PostgreSQL从小白到高手教程 - 第41讲:postgres表空间备份与恢复

PostgreSQL从小白到专家&#xff0c;是从入门逐渐能力提升的一个系列教程&#xff0c;内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容&#xff0c;希望对热爱PG、学习PG的同学们有帮助&#xff0c;欢迎持续关注CUUG PG技术大讲堂。 第41讲&#…

OpenCV-Python(33):SURF算法

目标 SUFR 是什么OpenCV 中的SURF 原理 学习了解过SIFT 算法后我们知道&#xff0c;它是对图像关键点进行检测和描述的&#xff0c;具有尺度不变的特性&#xff0c;但是这种算法的执行速度比较慢&#xff0c;人们需要速度更快的算法。2006年Bay,H.,Tuytelaars,T. 和Van Gool,…

盲盒小程序搭建,打造互联网电商模式

当前&#xff0c;盲盒作为年轻人的消费新选择&#xff0c;一度创下“销费奇迹”&#xff0c;深受年轻人的欢迎&#xff0c;成为了一种热门消费模式&#xff01;盲盒小程序是盲盒市场互联网时代发展下的产物&#xff0c;也是当下盲盒销售中的一种新形式。下面具体分析盲盒小程序…

从传统到智能:机器视觉检测赋能PCB行业数字化转型!

PCB板在现代电子设备中是一个重要的组成部分&#xff0c;它是用来集成各种电子元器件的信息载体。在电子领域中&#xff0c;PCB板有着广泛的应用&#xff0c;而它的质量直接影响到产品的性能。随着电子科技技术和电子制造业的发展&#xff0c;贴片元器件的体积 变小&#xff0c…

RT-Thread 中断管理接口

中断服务程序挂接 系统把用户的中断服务程序&#xff08;handler&#xff09;和指定的中断号关联起来&#xff0c;可调用如下的接口挂载一个新的中断服务程序&#xff1a; rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, void*param, char…

重磅!2024国家自然科学基金项目指南发布

1月11日&#xff0c;国家自然科学基金委员会正式发布《2024年度国家自然科学基金项目指南 》。 相比去年的指南&#xff0c;今年有如下变动&#xff1a; 申请规定具体如下&#xff1a; 各项目申请指南&#xff1a; 来源&#xff1a;国家自然科学基金委员会&#xff0c;爱科会易…

mysql忘记root密码后怎么重置

mysql忘记root密码后重置方法【windows版本】 重置密码步骤停掉mysql服务跳过密码进入数据库在user表中重置密码使用新密码登录mysql到此&#xff0c;密码就成功修改了&#xff0c;完结&#xff0c;撒花~ 重置密码步骤 当我们忘记mysql的密码时&#xff0c;连接mysql会报这样的…

无代码DIY图像检索

软件环境准备 可参见《HuggingFists-低代码玩转LLM RAG-准备篇》中的HuggingFists安装及Milvus安装。 流程环境准备 图片准备 进入HuggingFists内置的文件系统&#xff0c;数据源->文件系统->sengee_fs_settings_201创建Image文件夹将事先准备的多张相同或不同种类的图…

GPCR蛋白一般残基编号(Generic residue numbering)

文章目录 前言定义特殊情况参考连接 前言 在相应的文章中看到对于对于描述GPCR中的序列位置&#xff0c;往往在除了在当前蛋白的氨基酸序列序号意外&#xff0c;会在右上角标注一个类似于6 x 49的编号。经查这个编号有一个统一名称&#xff1a;Generic residue numbering。本文…

TCP/IP 网络模型

TCP/IP 网络通常是由上到下分成 4 层&#xff0c;分别是应用层&#xff0c;传输层&#xff0c;网络层和网络接口层。 应用层 应用层专注于为用户提供应用功能&#xff0c;比如 HTTP、FTP、Telnet、DNS、SMTP等。我们电脑或手机使用的应用软件都是在应用层实现。应用层是不用去关…

2024年T电梯修理证考试题库及T电梯修理试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年T电梯修理证考试题库及T电梯修理试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的T电…

【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 懒汉式&#xff08;Lazy Initialization&#xff09;&#xff1a; 双重检查锁定&#xff08;Double-Checked Locking&#xff09;…

21-链表-奇偶链表

这是链表的第21题&#xff0c;力扣链接。 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推。 …

什么是冒泡排序?如何实现?

一、是什么 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff0c;是一种计算机科学领域的较简单的排序算法 冒泡排序的思想就是在每次遍历一遍未排序的数列之后&#xff0c;将一个数据元素浮上去&#xff08;也就是排好了一个数据&#xff09; 如同碳酸饮料中二氧化碳的…

JavaWeb,HTML的学习

关于HTML、CSS、JavaScript HTML主要用于网页主体结构的搭建 CSS主要用于页面元素美化 JavaScript主要用于页面元素的动态处理 关于HTML 关于超文本 关于标记语言 HTML基础结构 html文件是浏览器负责解析和展示。html文件是纯文本文件&#xff0c;普通编辑工具都可以编辑。…

香港Web3:Web3的新热土

相关推荐点击查看TechubNews 随着区块链技术的快速发展&#xff0c;Web3的概念逐渐在全球范围内受到关注。作为亚洲的金融中心&#xff0c;香港在Web3领域也展现出了极大的热情和潜力。本文将探讨香港在Web3领域的发展现状、机遇与挑战。 一、香港Web3的发展现状 香港在Web3…

使用Python爬取小红书笔记与评论(js注入方式获取x-s)

文章目录 1. 写在前面2. 分析加密入口3. 使用JS注入4. 爬虫工程化 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感…

SQL-修改数据

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

RuntimeError: Placeholder storage has not been allocated on MPS device!解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…