带你见见红黑树-概念+插入篇

news2024/11/16 23:52:34

写的不好,见谅~

目录

概念理解

红黑树规则

AVL树与红黑树的相爱相杀

红黑树的插入时的上色与旋转。

不上色(shǎi)

情况一:空树

情况二:非空树,父节点为黑

上色(shǎi)

情况三:非空树,父节点为红色只需染色操作

上色+旋转

情况四:单旋

情况五:双旋情况


概念理解

红黑树红黑树,当然是红色和黑色的结点啦。

确实,我们的红黑树的各个结点中存放着一个标志颜色的标识。

template <class T>
struct RBTreeNode//红黑树结点
{
	RBTreeNode<T>* _left;//红黑树是一个三叉链的树形结构。
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;//<-------颜色标识
	T _data;    //数据的存放
	RBTreeNode(const T&data= T())
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		,_parent(nullptr)
		,_col(RED)
	{}
};

红黑树规则

红黑树既然是属于较为难的数据结构,那么他肯定也有着一些自己的树形规则。

在继承搜索二叉树的规则下自己新的规则如下

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

第1、2、3点都应该看得懂吧?

先解释我们的规则5(其实可有可无的):

第四点规则的意思是从任意一个结点开始计算,到叶子结点的黑色结点都相同。

为什么要定这些规则呢?应为这些规则保证了我们的最长路径不超过最短路径的2倍。黑色结点可以连续。

AVL树与红黑树的相爱相杀

不能说红黑树是AVL树的升级版本,打个比方吧:AVL树属于严厉的高中老师,红黑树属于慈爱的小学语文老师,啊怀念她。

AVL树只要不是一就要立刻旋转该树的结构。

红黑树给我们扩大了他的高低容忍差。

向下面哪个情况,AVL树早就不知道旋转多少次了。

但是红黑树就是可以容忍。

AVL树和红黑树的区别如下

  • AVL树是更加严格的平衡,因此可以提供更快的查找速度,一般读取查找密集型任务,适用AVL树。
  • 红黑树更适合于插入修改密集型任务。
  • 相对于要求严格平衡的AVL树来说,红黑树的旋转次数少,对于插入、删除操作较多的情况下,选择红黑树。
  • 红黑树的查询性能略微逊色于AVL树,但是红黑树在插入和删除上优于AVL树。

红黑树的插入时的上色与旋转。

前面插入位置寻找的逻辑与搜索二叉树逻辑一样

ret_type Insert(const T& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return make_pair(iterator(_root), true);
	}
	else
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (kot(cur->_data) > kot(data))
			{
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				cur = cur->_right;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		cur = new Node(data);
		Node* ret = cur;
		if (kot(parent->_data) < kot(cur->_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;
        //准备上色旋转

这上色就有讲究了!

我本以为吕布(AVL树)就天下无敌了,此人是谁的部将(红黑树)。

不上色(shǎi)

为了不会一插入就挨打,我们新结点初始状态就是红结点,为什么不是黑色呢?

如果我们插入黑色,在这棵树中有极大可能让这条路径比其他路径多一个黑色结点严重破坏结构,还得去检查前提路径黑结点情况,各种修改,各种调整,极其复杂。

所以我们都是默认新结点是红色结点,我们只需要检查脑袋上的父亲结点的颜色。

情况一:空树

那么我们的插入就是根结点,改变一下,_root链接,然后变个色就好了。

情况二:非空树,父节点为黑

这也很爽,由于父亲是黑色,我们插入一个红色结点不会破坏如何规则。

上色(shǎi)

情况三:非空树,父节点为红色只需染色操作

开始操作

cur为刚刚插入的结点,父节点为红,我们需要检查uncle结点为红色,我们将parent和uncle染成黑色保证父节点与uncle结点所在的子树黑数量相同

我们还需要将gf染成红色,保证了17结点的左右子树路径黑色结点相同

将gf指针赋给cur指针,其指针也一起变动。

如果cur->_parent为黑色就结束,p为红色就重复之前动作

 parent等于空的时候退出循环。

 无论是哪种情况,我们最好在循环后都加上一句_root=_col=BLACK,保证大树根为黑。

while (parent && parent->_col == RED)
{

}
_root->_col = BLACK;

上色+旋转

记住我们的uncle结点是很重要的,他是判别旋转的一个标志。

假设我们在上色的过程中uncle结点原本为空或者黑色的那可就麻烦了。

 第一次上色uncle为红允许改变。

 到了我们第二次操作的时候发现我们的parent为红色需要调整,但是uncle却是黑色的,不可以直接色,应为当前的gf开始每一条路径的黑结点数量都相同,如果我们一意孤行只改变p的颜色的话,会发现gf的左树路径比右数路径上的黑节点多一个,这是极其不正确的!破坏了红黑树的路径黑结点数量相同的原则。

 我们需要开始旋转了,根据cur确定是双旋还是单旋。图里的是左单选情况。

情况四:单旋

 旋转完后为了防止我们所看见的树也是一颗子树,我们恢复到旋转前黑色结点差,并且遵守规则,我们将p的颜色改变为BLACK,将gf改变为RED,

这样哪怕这就是是整颗红黑树,那他的根也是黑的,如果他只是红黑树的一个子树,我们的在各个路径上黑色结点的数量在插入后也是不改变的:插入前2个黑色结点,插入后依旧是2个。

看上图,这样我们30的左子树插入一个结点后旋转,但是插入后路径的黑结点都是2未该改变,这样我们就30结点左子树的插入不会影响与右子树路径黑结点数量的差。30插入前后左右子树路径的黑色结点都是2个黑

将grandfather指针赋值给cur后,重新标定cur与parent位置,但注意不能随意标定grandfather,为什么呢?我们的parent可能为空。

 所以只有在parent存在且为红的时候我们才可以对grandfather赋值。

情况五:双旋情况

改吧改吧红黑树

我们现在需要插入数据为13的结点,先链接到树中

 查看cur->parent是否为黑色或不存在,黑色停止,红色上色。

开始上色

uncle存在且为红

将grandfather指针赋值给cur,cur->parent._col==RED。继续循环,标定其他指针

 发现uncle这是为黑结点,我们需要开始旋转,发现gf->right==p p->left==cur所以我们这时候需要双旋存在,这棵树该先右旋在左旋

 变成概念图,好看多了吧~~

 先对20结点右旋

再对grandfather左旋

 最后将cur颜色该为BLACK,grandfather改变为RED。

红黑树插入完整代码:

pair<iterator, bool> Insert(const T& data)
{
	KeyOfT kot;
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return make_pair(iterator(_root), true);
	}
	else
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			parent = cur;
			if (kot(cur->_data) > kot(data))
			{
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				cur = cur->_right;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		cur = new Node(data);
		Node* ret = cur;
		if (kot(parent->_data) < kot(cur->_data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			assert(grandfather->_col == BLACK);
			Node* uncle = nullptr;
			if (parent == grandfather->_left)
			{
				uncle = grandfather->_right;

				//情况一:uncle存在,且为红。
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				//情况二+三:uncle不存在,或者存在为黑。
				else
				{
					//单旋
					if (parent->_left == cur)
					{
						RotateR(parent->_parent);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//双旋
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else /*if (parent == grandfather->_right)*/
			{
				uncle = grandfather->_left;

				//情况一:uncle存在,且为红。
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				//情况二+三:uncle存在,且为红。
				else
				{
					if (parent->_right == cur)
					{
						RotateL(parent->_parent);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//双旋
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
						int n = 0;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(iterator(ret), true);
	}
}
//......其他代码
public:

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	parent->_right = subRL;
	if (subRL)
	{
		subRL->_parent = parent;
	}
	Node* pparent = parent->_parent;

	parent->_parent = subR;
	subR->_left = parent;
	if (_root == parent)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subR;
		}
		else
		{
			pparent->_right = subR;
		}
		subR->_parent = pparent;
	}
}

void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}
	Node* pparent = parent->_parent;

	parent->_parent = subL;
	subL->_right = parent;
	if (_root == parent)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = subL;
		}
		else
		{
			pparent->_right = subL;
		}
		subL->_parent = pparent;
	}
}

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

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

相关文章

【Linux】深入理解文件系统

系列文章 收录于【Linux】文件系统 专栏 关于文件描述符与文件重定向的相关内容可以移步 文件描述符与重定向操作。 可以到 浅谈文件原理与操作 了解文件操作的系统接口。 想深入理解文件缓冲区还可以看看文件缓冲区。 目录 系列文章 磁盘 结构介绍 定位数据 抽象管理…

【Linux】MySQL 高级 SQL 语句 (二)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 MySQL 高级 SQL 语句 连接查询CREATE VIEW 视图UNION 联集交集值无交集值CASE空值(NULL) 和 无值() 的区别正则表达式 连接查询 mysql> select * from xjz; #xjz表格 ---…

Linux5.8 MySQL主从复制与读写分离

文章目录 计算机系统5G云计算第四章 LINUX MySQL主从复制与读写分离一、概述及原理1&#xff09;什么是读写分离2&#xff09;为什么要读写分离呢3&#xff09;什么时候要读写分离4&#xff09;主从复制与读写分离5&#xff09;mysql支持的复制类型6&#xff09;主从复制的工作…

Rust语言从入门到入坑——(5)Rust 所有权

文章目录 0 引入1、所有权2、内存和分配3、移动与克隆3.1、移动3.2、克隆 4、引用与租借4.1、引用4.1、垂悬引用 5、函数中变量5.1 参数变量5.2 、返回值变量 0 引入 主要介绍Rust所有权的知识&#xff0c;涉及到变量的作用域&#xff0c;内存释放机制&#xff0c;移动&#x…

Python|Pyppeteer启动浏览器窗口,右侧出现空白区域怎么解决?(13)

前言 本文是该专栏的第13篇,结合优质项目案例持续分享Pyppeteer的干货知识,记得关注。 有些同学可能在使用pyppeteer的时候,在配置项里面,明明已经设置好了窗口最大化,而启动Chromium窗口,打开的窗口最右侧却是一大片空白区域,具体如下图所示: 那么,出现上述情况,需…

AutoGPT 英文版安装过程

自从2022年11月chatGPT的发布3.0GPT大模型&#xff0c;在中国掀起一股AI学习热潮&#xff0c;国内百度2023年4月份发布文心一言&#xff0c;把AI推上另一个高潮&#xff0c;最直接的是问答&#xff0c;我输入一句话&#xff0c;AI帮生成一段文字或一个视频&#xff0c;但是国内…

畅捷通T+ 反序列化漏洞复现(QVD-2023-13615)

0x01 产品简介 畅捷通 T 是一款基于互联网的新型企业管理软件&#xff0c;功能模块包括&#xff1a;财务管理、采购管理、库存管理等。主要针对中小型工贸和商贸企业的财务业务一体化应用&#xff0c;融入了社交化、移动化、物联网、电子商务、互联网信息订阅等元素。 0x02 漏…

Chat2DB数据AI工具开源!对数据分析师行业影响如何?

大家好&#xff0c;我是千与千寻&#xff0c;千寻目前在互联网公司担任算法工程师&#xff0c;也要经常性的和数据打交道。那么数据都存放在哪里&#xff1f;当然是数据库啦&#xff01; 说到数据库&#xff0c;我们就不得不提到一种编程语言——SQL数据语言&#xff0c;后端程…

2023年最新项目管理工具排名推荐,助你提升项目效率!

在当今快速发展的互联网时代&#xff0c;项目管理工具已经成为了越来越多企业和团队必不可少的工具之一。好的项目管理工具能够帮助团队更加高效地协同工作&#xff0c;提高工作效率&#xff0c;节省时间和成本&#xff0c;从而使得整个项目可以更快地达成预期目标。现在让我们…

微信为什么使用 SQLite 保存聊天记录?

概要 SQLite 是一个被大家低估的数据库&#xff0c;但有些人认为它是一个不适合生产环境使用的玩具数据库。事实上&#xff0c;SQLite 是一个非常可靠的数据库&#xff0c;它可以处理 TB 级的数据&#xff0c;但它没有网络层。接下来&#xff0c;本文将与大家共同探讨 SQLite 在…

【Diffusion模型系列1】DDPM: Denoising Diffusion Probabilistic Models

0. 楔子 Diffusion Models(扩散模型)是在过去几年最受关注的生成模型。2020年后&#xff0c;几篇开创性论文就向世界展示了扩散模型的能力和强大: Diffusion Models Beat GANs on Image Synthesis(NeurIPS 2021 Spotlight, OpenAI团队, 该团队也是DALLE-2的作者)[1] Various…

阿里云国际站代理商:如何优化阿里云服务器的性能和响应速度?有哪些调优策略和建议?

随着互联网的发展&#xff0c;阿里云服务器已经成为很多企业和个人的首选解决方案。然而&#xff0c;面对不断增长的需求和复杂的网络环境&#xff0c;如何优化阿里云服务器的性能和响应速度&#xff0c;提高用户体验&#xff0c;是很多用户关心的问题。本文将从以下几个方面&a…

上海阿里云代理商:如何保护阿里云服务器中的敏感数据?有哪些加密和访问控制措施?

如何保护阿里云服务器中的敏感数据&#xff1f;有哪些加密和访问控制措施&#xff1f;   一、阿里云服务器安全概述   阿里云服务器作为云计算服务的主要产品&#xff0c;其安全性备受用户关注。在实际使用中&#xff0c;保护服务器中的敏感数据是至关重要的&#xff0c;而…

Tkinter之GUI界面布局介绍

Tkinter之GUI界面布局介绍 关于Python 的Tkinter窗口基础可参见https://blog.csdn.net/cnds123/article/details/127227651 Tkinter 本身没有提供拖拽放置控件的方式创建 GUI 界面&#xff0c;而是提供了pack、grid和place三种几何管理器&#xff08;geometry manager&#x…

canvas详解05-变形

几何变换 canvas现在被大量地运用于游戏等动画领域,最主要的归功于它提供的一系列几何变换方法,使得动画更加地容易。所以其几何变换是非常重要的一节。 在本教程前面的部分中,我们已经了解了 Canvas 网格和坐标空间。到目前为止,我们只是根据我们的需要使用默认的网格,改…

Unity - 记一次,使用 RenderDoc 调试 渲染 异常 的过程

文章目录 vertex shader - rawfragment shader - raw调试RDC的shader准备选项 - remote - 不要选local先查看 texture, sampler, ubo 数据调试&#xff1a;输出原始的法线数据调试&#xff1a;输出原始法线0\~1>-1\~1&#xff0c;并应用法线强度的法线调试&#xff1a;输出世…

强化学习从基础到进阶-常见问题和面试必知必答[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战

【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧&#xff08;调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍&#xff1a;【强化学习原理项目专栏】必看系列&#xff1a;单智能体、多智能体算法原理项目实战、相关技巧…

一文了解RabbitMQ安装使用

什么是RabbitMQ? 官网&#xff1a;Messaging that just works — RabbitMQ RabbitMQ是一种开源的消息中间件软件&#xff0c;用于构建可扩展的分布式应用程序。它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c;这是一种网络协议&#xff0c;用于在应用程序之…

RIS 系列:TransVG: End-to-End Visual Grounding with Transformers 论文阅读笔记

RIS 系列&#xff1a;TransVG: End-to-End Visual Grounding with Transformers 论文阅读笔记 一、Abstract二、引言三、相关工作3.1 视觉定位两阶段方法单阶段方法 3.2 Transformer视觉任务中的 Transformer视觉-语言任务中的 Transformer 四、视觉定位中的 Transformer4.1 基…

【网络协议详解】——IPv4(学习笔记)

目录 &#x1f552; 1. IPv4地址概述&#x1f552; 2. 分类编址&#x1f552; 3. 划分子网&#x1f558; 3.1 概述&#x1f558; 3.2 如何实现&#x1f558; 3.3 无分类编址&#x1f558; 3.4 应用规划&#x1f564; 3.4.1 定长的子网掩码FLSM&#xff08;Fixed Length Subnet …