首页 > 其他分享 >红黑树的变色与旋转

红黑树的变色与旋转

时间:2023-11-19 17:01:29浏览次数:42  
标签:cur parent 旋转 插入 col kv 红黑树 变色 节点

一、红黑树的概念

红黑树是一种二叉搜索树,但在每个节点上增加一个存储位表示节点的颜色,可以是Red或者Black。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

二、红黑树的性质

红黑树的变色与旋转_旋转

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

三、红黑树节点的定义

enum Colour//节点的颜色
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode//红黑树节点
{
	RBTreeNode<K, V>* _parent;//父指针
	RBTreeNode<K, V>* _left;//左指针
	RBTreeNode<K, V>* _right;//右指针
	Colour _col;//颜色
	pair<K, V> _kv;//键值对

	RBTreeNode(const pair<K, V>& kv)//构造函数
		:_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_col(RED)
		,_kv(kv)
	{}
};

四、红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1、按照二叉搜索树的规则插入新节点

bool insert(const pair<K, V>& kv)//插入
	{
		if (_root == nullptr)//如果树为空
		{
			_root = new Node(kv);//直接让根节点指向新节点
			_root->_col = BLACK;//根节点为黑色
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)//寻找插入位置
		{
			parent = cur;//记录父节点
			if (cur->_kv.first < kv.first)//插入的值比当前节点大
			{
				cur = cur->_right;//往右走
			}
			else if (cur->_kv.first > kv.first)//插入的值比当前节点小
			{
				cur = cur->_left;//往左走
			}
			else//插入的值与当前节点相同
			{
				return false;//插入失败
			}
		}
		//找到插入的位置了,开始插入
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)//插入到父节点的右边
		{
			cur->_parent = parent;
			parent->_right = cur;
		}
		else if (parent->_kv.first > kv.first)//插入到父节点的左边
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
  
		//调整颜色
    //......
  
		_root->_col = BLACK;//根节点的颜色一定是黑色
		return true;
	}

2、插入新节点后,红黑树的性质可能被破坏了,此时需要调整红黑树

新节点的默认颜色是红色,因此:如果新节点的父节点是黑色,则没有违反红黑树的任何性质,不需要调整;但是当新节点的父节点是红色时,就违反了性质3不能有连续的红色节点,此时需要对红黑树分情况进行讨论。

规定:cur为新节点,p为新节点的父节点,g为新节点的祖父节点,u为新节点的叔叔节点。

(1)cur为红,p为红,g为黑,u存在且为红

此时只需要通过变色就能解决。

注意:此处看到的树,可能是一颗完整的树,也可能是一棵子树

红黑树的变色与旋转_红黑树_02

调整完成后,此时g变成了红色,如果g有父节点并且父节点为红色,此时需要继续向上调整。

红黑树的变色与旋转_红黑树_03

(2)cur为红,p为红,g为黑,u不存在或者存在且为黑

此时我们需要通过旋转+变色的方法来调整,调整完毕后,不需要再继续向上调整,因为我们调整后,最上面的节点是黑色,此时不论它的父节点是红色还是黑色,都不需要再继续调整了。

单旋后,cur和u不变色,p变为黑色,g变为红色。


红黑树的变色与旋转_变色_04

双旋后,p和u不变色,cur变为黑色,g变为红色。

红黑树的变色与旋转_红黑树_05

关于旋转的更详细内容,可以参考:AVL树的旋转_wx635fd1fc02a16的技术博客_51CTO博客

完整的插入代码:

bool insert(const pair<K, V>& kv)//插入
	{
		if (_root == nullptr)//如果树为空
		{
			_root = new Node(kv);//直接让根节点指向新节点
			_root->_col = BLACK;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)//寻找插入位置
		{
			parent = cur;//记录父节点
			if (cur->_kv.first < kv.first)//插入的值比当前节点大
			{
				cur = cur->_right;//往右走
			}
			else if (cur->_kv.first > kv.first)//插入的值比当前节点小
			{
				cur = cur->_left;//往左走
			}
			else//插入的值与当前节点相同
			{
				return false;//插入失败
			}
		}
		//找到插入的位置了,开始插入
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)//插入到父节点的右边
		{
			cur->_parent = parent;
			parent->_right = cur;
		}
		else if (parent->_kv.first > kv.first)//插入到父节点的左边
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		//调整颜色
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;//祖父节点
			if (grandfather->_left == parent)//新插入的节点在父节点的左边
			{
				Node* uncle = grandfather->_right;//叔叔节点
				if (uncle && uncle->_col == RED)//叔叔节点不为空并且是红色
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
				}
				else//叔叔节点为空或者叔叔节点是黑色
				{
					if (cur == parent->_left)//右单旋
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//左右双旋
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;//旋转后就不需要继续调整,跳出循环
				}
			}
			else//新插入的节点在父节点的右边
			{
				Node* uncle = grandfather->_left;//叔叔节点
				if (uncle && uncle->_col == RED)//叔叔节点不为空并且是红色
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
				}
				else//叔叔节点为空或者叔叔节点是黑色
				{
					if (cur == parent->_right)//左单旋
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else//右左双旋
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;//旋转后就不需要继续调整,跳出循环
				}
			}
			//继续向上调整
			cur = grandfather;
			parent = grandfather->_parent;
		}
		_root->_col = BLACK;//根节点的颜色一定是黑色
		return true;
	}

五、红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(log2N),红黑树不追求绝对平衡,其只需要保证最长路径不超过最短路径的2倍,相对AVL树而言,降低了插入和旋转的次数,所以在经常进行增删查改的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。



完结。。。。。








标签:cur,parent,旋转,插入,col,kv,红黑树,变色,节点
From: https://blog.51cto.com/u_15855358/8474704

相关文章

  • 罗德里格旋转公式(Rodrigues' rotation formula)
    https://zhuanlan.zhihu.com/p/115276808   ......
  • HighChart坐标轴标签旋转及刻度线调整+格式化小数点
    标签及字符串格式化|Highcharts使用教程需求:坐标轴标签旋转需要将X轴刻度标签旋转X度,突出刻度线长度、宽度、颜色,使其整体上更美观,需要保留小数点。分析: 坐标轴标签旋转需要用rotation来控制,刻度线需分别用tickWidth(刻度宽度)tickLength(刻度长度)tickColor(刻度颜色)来控制。而格......
  • 微信小程序canvas 设置旋转css 不生效
    问题项目中有使用canvas生成条码(一维码)的功能,使用的插件wxbarcode来生成的,但是项目需求的条码是要竖向的,插件的生成的是横向的,不知道是否有参数去控制,当时图省事想着直接用css旋转一下好了,在模拟器上看到的确实也没有问题,但是在真机上就出问题,没有旋转,还发生了偏移解决开始一......
  • 红黑树插入节点的模拟实现
    要学习红黑树节点的插入那么首先就要了解什么是红黑树,以及红黑树的特点。红黑树的特点本来AVL树已经很厉害了,但是红黑树的总体效率略比1AVL树高。高的大体原因。我们先来看一下红黑树和AVL树的区别。AVL树严格的保证了左子树和右子树的高度差不超过1,而红黑树则是保证了最长路径不超......
  • vue+css实现的伪3d旋转罐+液位动态变化
    话不多说先看效果:设计思路:罐是做了三个位置(中=>左,左=>右,右=>中)的动画效果,每个罐轮流使用一次,来实现旋转的效果。中间的光亮做了个变形延迟。罐的透明效果是使用了三层,即最底层是粒子不透明图片,中层是液体组件,最上层是罐体png图片。都是用了绝对定位,请务必设置好位置。液体组......
  • 直播app系统源码,图片Loading旋转动画效果
    直播app系统源码,图片Loading旋转动画效果anim文件下的动画xml: <?xmlversion="1.0"encoding="utf-8"?><rotatexmlns:android="http://schemas.android.com/apk/res/android"  android:fromDegrees="0"//旋转的起始角度  android:toDegrees=&......
  • 使用pillow对图像进行旋转和添加高斯白噪声
    高斯白噪声defadd_gaussian_noise(image,mean=0,std=25):"""给图像添加高斯噪声。:paramimage:输入图像:parammean:噪声均值:paramstd:噪声标准差:return:添加噪声后的图像"""image=np.array(image)h,w,c=im......
  • AVL树节点插入方式解析(单旋转和双旋转)
    AVL树的规则在学习AVL树插入节点的方式之前,我们首先要理解为什么要出现AVL树,首先我们要知道的是AVL树是在二叉搜索树的基础上增加一些限制条件才完成的。那么AVL树就是为了处理二叉搜索树的缺点而出现的一棵树,那么普通的二叉搜索树的缺点是什么呢?假如往树中插入的元素有序或者接近......
  • CSS3 实现动态旋转加载样式
    CSS3实现动态旋转加载样式要使用CSS3创建一个动态旋转加载样式,你可以使用CSS动画和旋转变换。下面是一个简单的示例:HTML:<divclass="loader"></div>CSS:.loader{width:50px;height:50px;border:4pxsolid#3498db;border-top:4pxsolidtransparent;......
  • 四元数旋转
    参考了:一个对四元数旋转的简单推导-知乎(zhihu.com)       周期为4Π,这是算出的结果。就是0-720。作为特例绕莫格轴转动时,旋转(单位)四元数w的顺序为正负负正,其他分量的顺序为正正负负,且平方和为1。 可在此转换模式测试。 ......