初识C++之红黑树

Posted 网络天使莱娜酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识C++之红黑树相关的知识,希望对你有一定的参考价值。

目录

一、红黑树的概念

二、红黑树的性质

三、红黑树的平衡情况

四、红黑树的模拟实现

1. uncle存在且为红

2. u不存在/存在且为黑->单旋

2.1 p节点在g节点的左侧,c节点在p节点的左侧—左左->右单旋

2.2 p节点在g节点的右侧,c节点在p节点的右侧—右右->左单旋

 2.3 单旋总结

3. u不存在/存在且为黑->双旋

3.1 p节点在g节点的左侧,c节点在p节点的右侧——左右->左右双旋

 3.2 p节点在g节点的右侧,c节点在p节点的左侧——右左->右左双旋

3.3 双旋总结

5.红黑树调整总结

6.红黑树数据插入模拟实现


一、红黑树的概念

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

有人可能会疑惑,AVL树也是一种二叉搜索树,它能够保持高度平衡使得搜索效率为O(longN)。既然已经有AVL树了,那为什么还会有红黑树呢?从描述上看,它的搜索效率甚至可能比AVL树低一点。

其实是因为AVL树为了保持高度平衡,是通过旋转达到的。而每次旋转都会进行节点的调整。如果在数据量足够庞大的情况下,AVL树需要的调整次数也会随之增长。而红黑树则不同,红黑树对平衡要求并没有那么严格,仅仅只是要求最长路径不超过最短路径的两倍,这就导致红黑树在插入时需要的旋转次数会比AVL树少很多。假设现在有10亿数据,AVL树在插入时可能需要进行5000w次旋转,搜索30次;而红黑树则可能只需要2500w次旋转,搜索60次。从CPU的角度来看,30次和60次所消耗的时间差几乎可以忽略不计。但是在插入时红黑树的效率就比AVL树高很多。

因此,在现实中,如果需要使用到二叉搜索树的场景,一般都是优先使用红黑树,AVL树已经很少使用了,绝大部分情况下被红黑树所替代。

二、红黑树的性质

一棵红黑树,通常具有如下5个性质。

(1)每个结点不是红色就是黑色

(2)根节点时黑色的

(3)如果一个结点时红色的,则它的两个孩子节点是黑色的。(注意,这里只要求红色节点的两个子结点要为黑色,并没有要求黑色节点的子结点要为红色,所以黑色结点的子结点也可能是黑色)

(4)对于每个结点,从该结点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

(5)每个叶子结点都是黑色的(此处的叶子节点指的是空节点)

上图就是一个红黑树。它的每条路径上的黑色结点都是相同的。

现在我们知道红黑树的最长路径不超过最短路径2倍。那么红黑树是如何做到这一点的呢?其实就是依托于性质3和性质4。

首先,性质3就规定了在一个红黑树中,不存在连续的红色节点。而性质4又规定每条路径上都有相同数目的黑色节点。通过这两个性质就可以知道,在一条路径上,红色结点的数量必然小于等于黑色结点。因此可以推断红黑树的最短路径就是全黑结点或者说是红色结点最少的路径;而最长路径就是红色和黑色结点相间的路径。

三、红黑树的平衡情况

与AVL树的高度平衡不同,红黑树是一种近似平衡。

红黑树的最优情况就是“左右平衡”。即路径全黑每条路径都是红黑相间。而最坏情况就是“左右高度不平衡”,即一边全黑且另一边黑红相间

四、红黑树的模拟实现

红黑树在插入时,势必会遇到需要进行调整的情况。而需要进行调整的情况,又分为三种。

在这里,首先假定插入的节点为curcur的父节点为parentparent父节点为groundfather节点parent的相邻节点为uncle节点

分别简写为c、p、g、u节点 :

 我们还要有一个认知,那就是在红黑树中,cur节点默认为红色。这就说明,如果需要调整,那么cur的parent节点必为红;又因为红黑树中不能有连续的红色节点,所以parent的父节点即g节点必为黑

因此,cur的p节点和g节点颜色在需要调整时都是固定的。唯一的变数就在于uncle节点。所以,以下所有情况都是针对于uncle节点及cur节点在p节点的位置来分类的。

1. uncle存在且为红

这种情况的红黑树如下图:

在这种情况下,要进行调整很简单,就是将p和u节点设置为黑,并将g节点设置为红即可。如果g为根节点,则重新设置为黑

要将g分为两种情况的原因还是,当g为根节点时,g节点默认为黑色,此时直接设置为黑色即可。但如果g不是根节点,那么该子树就可能需要以g为子节点继续向上调整。如以下情况:

在上图中,g就不是根节点,在调整完后,需要以g为子节点继续向上调整。向上调整时可能遇到其他情况,例如u为黑或u不存在等。这里演示的情况只是最简单的情况。

2. u不存在/存在且为黑->单旋

和AVL树一样,当插入节点时,如果p节点和c节点都在同一侧,则进行单旋。这就会分成两种情况

2.1 p节点在g节点的左侧,c节点在p节点的左侧—左左->右单旋

首先看u不存在的情况:

如果大家学习了AVL树,就会发现这种情况其实就和AVL树的右单旋情况很像。在这里,要调整红黑树,就可以以g节点为基点,进行一次右单旋。让g节点的左子节点指向p节点,p节点的右子节点指向g节点,然后更新对应节点的指针,并让g节点的原父节点指向p节点。

在节点调整完后,因为p节点此时代替g,颜色先修改为黑; 而g节点成为p节点的子节点,颜色修改为红色

 再来看u存在且为黑的情况:

这种情况一般原生不存在,而是由情况一,即u存在且为红时进行调整而来。

此时该树就违反了规则3——不能出现连续的红色节点。

要调整这棵树也很简单,同样的,以g为基点进行右单旋。让p的左子节点指向g,g的左子节点指向p的右子节点,然后分别调整p、g和p的原右子节点的父指针,并让g的原父节点指向p。

当旋转完后,将g的颜色修改为红,p的颜色修改为黑

通过如上方式调整后,就可以将树重新调整为红黑树。

void RotateR(Node* grandfather)//右单旋

	Node* parent = grandfather->_left;
	Node* subLR = parent->_right;

	parent->_right = grandfather;//p的右子节点指向g
	grandfather->_left = subLR;//g的左子节点指向p的右子节点

	if (subLR)
		subLR->_parent = grandfather;//subLR存在,则更新它的父指针指向g

	Node* ppNode = grandfather->_parent;
	parent->_parent = ppNode;//p的父指针指向g的父节点
	grandfather->_parent = parent;//g的父指针指向p

	if (grandfather == _root)//g节点是根节点,更新根节点
	
		_root = parent;
		_root->_parent = nullptr;
	
	else//p不是根节点,更新ppNode的指向
	
		if (ppNode->_left == grandfather)//parent在它的左边
			ppNode->_left = parent;
		else if (ppNode->_right == grandfather)//parent在它的右边
			ppNode->_right = parent;
		else
			assert(false);//到这里,说明程序有问题
	

	grandfather->_col = RED;//更新p和g的节点颜色
	parent->_col = BLACK;

2.2 p节点在g节点的右侧,c节点在p节点的右侧—右右->左单旋

u不存在时的情况很简单,就不再演示。

假设有如下经过情况一调整而来的红黑树:

可以看到,此时p在g的右侧,c在p的右侧。在这种情况下,就需要使用左单旋。让p的右子节点指向g,g的右子节点指向p的左子节点,然后更新g、p和p的左子节点的父指针,最后让g的原父节点指向p。

旋转完后,同样的,将g的颜色调整为红色,p的颜色调整为黑色

void RotateL(Node* grandfather)//左单旋

	Node* parent = grandfather->_right;
	Node* subRL = parent->_left;

	parent->_left = grandfather;//p的左子节点指向g
	grandfather->_right = subRL;//g的右子节点指向p的左子节点

	if (subRL)
		subRL->_parent = grandfather;//subRL存在,更新父指针

	Node* ppNode = grandfather->_parent;
	parent->_parent = ppNode;//p的父指针指向g的原父节点
	grandfather->_parent = parent;//g的父指针指向p

	if (grandfather == _root)//grandfather是根节点,更新根节点
	
		_root = parent;
		_root->_parent = nullptr;
	
	else//g不是根节点
	
		if (ppNode->_left == grandfather)//p在左
			ppNode->_left = parent;
		else if (ppNode->_right == grandfather)//p在右
			ppNode->_right = parent;
		else//走到这里,说明程序有问题
			assert(false);
	

	grandfather->_col = RED;
	parent->_col = BLACK;//更新g和p的节点颜色

 2.3 单旋总结

由此,可以总结出红黑树的单旋情况。

当p在g的左侧,c在p的左侧——左左时,采用右单旋

当p在g的右侧,c在p的右侧——右右时,采用左单旋

当旋转完成后,将g的颜色改为红色,p的颜色改为黑色

3. u不存在/存在且为黑->双旋

和AVL树一样,红黑树也会存在需要双旋的情况。但是红黑树的旋转都是建立在u不存在/存在且为黑的条件之上的。

3.1 p节点在g节点的左侧,c节点在p节点的右侧——左右->左右双旋

如下图一个调整而来的红黑树:

经过向上调整后,g被视为c节点。此时,p节点在g节点的左侧,c节点在p节点的右侧。在这种情况下,单次旋转就不再生效,和AVL树一样,需要进行双旋。

首先是以p为基点进行左单旋。让c的左子节点指向p,p的右子节点指向c的左子节点,更新p、c和c的左子节点的父指针,再让p的原父节点g指向c。最后将c和p的颜色都调整为红。

第二步就是以g为基点进行右单旋。让c的右子节点指向g,g的左子节点指向c的右子节点,更新c、g和c的右子节点的父指针,最后让g的原父节点指向c。

旋转完成后,将g的颜色调整为红色,c的颜色调整为黑色

void RotateLR(Node* parent)//左右双旋

	Node* grandfather = parent->_parent;
	Node* cur = parent->_right;

	RotateL(parent);//以p为基点左单旋
	RotateR(grandfather);//以g为基点右单旋

	cur->_col = BLACK;
	grandfather->_col = RED;//更新c、g节点颜色

 3.2 p节点在g节点的右侧,c节点在p节点的左侧——右左->右左双旋

如下图一个经过情况一调整而来的红黑树:

可以看到,经过情况一调整后的该树,它的p节点在g节点的右侧,c节点在p节点的左侧。此时,就需要进行两次旋转。

首先以p为基点进行右单旋。让c的右子节点指向p,p的左子节点指向c的右子节点,分别更新c、p和c的右子节点的父指针,最后让p的原父节点g指向c。

旋转完后,将p和c的颜色都调整为红色:

第二步就是以g为基点进行左单旋。让c的左子节点指向g,g的右子节点指向c的左子节点,更新g、c和c的左子节点的父指针,最后让g的原父节点指向c。

旋转完后,将g的颜色调整为红色,c的颜色新调整为黑色

void RotateRL(Node* parent)//右左双旋

	Node* grandfather = parent->_parent;
	Node* cur = parent->_left;

	RotateR(parent);//以p为基点右单旋
	RotateL(grandfather);//以g为基点左单旋

	cur->_col = BLACK;
	grandfather->_col = RED;//更新c、g节点颜色

3.3 双旋总结

通过上面的归类,就可以整理出红黑树需要进行双旋调整的情况。

(1)当p为g的左侧,c为p的右侧(左右)时,需要进行左右双旋

(2)当p为g的右侧,c为p的左侧(右左)时,需要进行右左双旋

当完成两次旋转后,g的颜色调整为红色,c的颜色调整为黑色

5.红黑树调整总结

通过上面的阐述,红黑树如何调整就很清楚了。红黑树要调整,首先会经过“uncle存在且为红”的情况,在经过该情况调整后,才会出现“uncle不存在/存在且为黑”的第二种调整情况。

而“uncle不存在/存在且为黑”的情况有根据p和c的位置出现4中不同调整方案。

(1)当p在g的左侧,c在p的左侧——左左时,采用右单旋

(2)当p在g的右侧,c在p的右侧——右右时,采用左单旋

在只需进行单旋的情况下, 在旋转完后要将g的颜色调整为红,p的颜色调整为黑

(3)当p为g的左侧,c为p的右侧(左右)时,需要进行左右双旋

(4)当p为g的右侧,c为p的左侧(右左)时,需要进行右左双旋

在需要进行双旋的情况下,在两次旋转完后,需要将g的颜色调整为红色,c的颜色调整为黑色

如果仔细观察双旋情况就会发现,其实双旋改修改的节点的位置和单旋的是一样的,双旋中的c节点其实就是单旋中的p节点,只是图中为了显示两次旋转后的差异,没有修改命名。

如果大家已经学习过了AVL树,就会发现红黑树的调整方法和调整情况绝大部分都是一样的,都是左左->右单旋;右右->左单旋;左右->左右双旋;右左->右左双旋。区别一个在于红黑树在旋转前,会经过一次“uncle存在且为红”的调整;另一个就是AVL树需要更新平衡因子,而红黑树没有平衡因子,而是更新节点颜色。

6.红黑树数据插入模拟实现

有了上面红黑树面对不同情况的旋转调整,就可以写出红黑树的插入了:

#pragma once
#include<iostream>
#include<cassert>
#include<ctime>

using namespace std;

namespace MyRBTree

	enum Colour//用枚举定义颜色
	
		RED,
		BLACK
	;

	template<class K, class V> 
	struct RBTreeNode
	
		pair<K, V> _kv;//kv模型
		RBTreeNode* _left;//左子结点
		RBTreeNode* _right;//右子节点
		RBTreeNode* _parent;//父节点
		Colour _col;//节点颜色

		RBTreeNode(const pair<K, V>& kv)//构造函数
			: _kv(kv)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _col(RED)//节点颜色默认给红色
		
	;

	template<class K, class V>
	class RBTree
	
		typedef RBTreeNode<K, V> Node;
	public:
		RBTree()
		

		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 (kv.first < cur->_kv.first)//传进来的键值小于节点键值,向左走
				
					parent = cur;
					cur = cur->_left;
				
				else if (kv.first > cur->_kv.first)//传进来的键值大于节点键值,向右走
				
					parent = cur;
					cur = cur->_right;
				
				else//走到这里,说明传进来的键值与节点键值相等,返回false
				
					return false;
				
			

			cur = new Node(kv);//走到这里,说明找到插入位置,节点颜色默认设置为RED
			cur->_col = RED;

			if (kv.first < parent->_kv.first)//键值小于父节点键值,插入在左边
			
				parent->_left = cur;
				cur->_parent = parent;
			
			else if (kv.first > parent->_kv.first)//键值大于父节点键值, 插入在右边
			
				parent->_right = cur;
				cur->_parent = parent;
			
			else//走到这里,说明程序出现问题
				assert(false);

			//调整红黑树
			while (parent && parent->_col == RED)//如果parent的颜色为黑色,就无需调整
			
				Node* grandfather = parent->_parent;//祖父节点
				if (grandfather->_left == parent)
				
					Node* uncle = grandfather->_right;//父节点在左,叔叔节点在右
					
					if (uncle && uncle->_col == RED)//情况一:uncle存在且为红,调整p、u、g三个节点的颜色和修改指针
					
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						cur = grandfather;
						parent = cur->_parent;//更新c、p两个个节点,g和u节点等循环上去重新创建				
					
					else if (uncle == nullptr || uncle->_col == BLACK)//情况二:uncle不存在/存在且为黑。
																	  //此处分为左左和左右两种情况
						if (cur == parent->_left)//左左,右单旋
						
							RotateR(grandfather);
						
						else if (cur == parent->_right)//左右,左右双旋
						
							RotateLR(parent);
						

						break;
					
				
				else if (grandfather->_right == parent)
				
					Node* uncle = grandfather->_left;//父节点在右,叔叔节点在左
					
					if (uncle && uncle->_col == RED)//情况一:uncle节点存在且为红
					
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						cur = grandfather;
						parent = cur->_parent;//更新c、p两个节点,g和u等循环上去重新创建					
					
					else if (uncle == nullptr || uncle->_col == BLACK)//情况二:uncle不存在/存在且为黑
					
						if (cur == parent->_right)//右右,左单旋
						
							RotateL(grandfather);
						
						else if (cur == parent->_left)//右左,右左双旋
						
							RotateRL(parent);
						

						break;
					
				
				else
				
					assert(false);//到这里,说明程序有问题
				
			
			_root->_col = BLACK;//默认将根节点设置为黑

			return true;
		

		void RotateR(Node* grandfather)//右单旋
		
			Node* parent = grandfather->_left;
			Node* subLR = parent->_right;

			parent->_right = grandfather;//p的右子节点指向g
			grandfather->_left = subLR;//g的左子节点指向p的右子节点

			if (subLR)
				subLR->_parent = grandfather;//subLR存在,则更新它的父指针指向g

			Node* ppNode = grandfather->_parent;
			parent->_parent = ppNode;//p的父指针指向g的父节点
			grandfather->_parent = parent;//g的父指针指向p

			if (grandfather == _root)//g节点是根节点,更新根节点
			
				_root = parent;
				_root->_parent = nullptr;
			
			else//p不是根节点,更新ppNode的指向
			
				if (ppNode->_left == grandfather)//parent在它的左边
					ppNode->_left = parent;
				else if (ppNode->_right == grandfather)//parent在它的右边
					ppNode->_right = parent;
				else
					assert(false);//到这里,说明程序有问题
			

			grandfather->_col = RED;//更新p和g的节点颜色
			parent->_col = BLACK;
		

		void RotateL(Node* grandfather)//左单旋
		
			Node* parent = grandfather->_right;
			Node* subRL = parent->_left;

			parent->_left = grandfather;//p的左子节点指向g
			grandfather->_right = subRL;//g的右子节点指向p的左子节点

			if (subRL)
				subRL->_parent = grandfather;//subRL存在,更新父指针

			Node* ppNode = grandfather->_parent;
			parent->_parent = ppNode;//p的父指针指向g的原父节点
			grandfather->_parent = parent;//g的父指针指向p

			if (grandfather == _root)//grandfather是根节点,更新根节点
			
				_root = parent;
				_root->_parent = nullptr;
			
			else//g不是根节点
			
				if (ppNode->_left == grandfather)//p在左
					ppNode->_left = parent;
				else if (ppNode->_right == grandfather)//p在右
					ppNode->_right = parent;
				else//走到这里,说明程序有问题
					assert(false);
			

			grandfather->_col = RED;
			parent->_col = BLACK;//更新g和p的节点颜色
		

		void RotateLR(Node* parent)//左右双旋
		
			Node* grandfather = parent->_parent;
			Node* cur = parent->_right;

			RotateL(parent);//以p为基点左单旋
			RotateR(grandfather);//以g为基点右单旋

			cur->_col = BLACK;
			grandfather->_col = RED;//更新c、g节点颜色
		

		void RotateRL(Node* parent)//右左双旋
		
			Node* grandfather = parent->_parent;
			Node* cur = parent->_left;

			RotateR(parent);//以p为基点右单旋
			RotateL(grandfather);//以g为基点左单旋

			cur->_col = BLACK;
			grandfather->_col = RED;//更新c、g节点颜色
		

	private:
		Node* _root;
	;

对于红黑树,对当前的我们来说只需要掌握如何插入构建一棵红黑树即可,对于红黑树的删除、替换等操作暂时不用过多了解。

C++从入门到入土第二十二篇:数据结构之红黑树

红黑树



一、红黑树

红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

简介

红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。
红黑树是一种平衡二叉查找树的变体,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉树(AVL),但 对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。
由于每一棵红黑树都是一颗二叉排序树,因此,在对红黑树进行查找时,可以采用运用于普通二叉排序树上的查找算法,在查找过程中不需要颜色信息。

性质

红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
 性质1. 结点是红色或黑色。
 性质2. 根结点是黑色。
 性质3. 所有叶子都是黑色。(叶子是NIL结点)
 性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
 性质5. 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。
这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。
是性质4导致路径上不能有两个连续的红色结点确保了这个结果。最短的可能路径都是黑色结点,最长的可能路径有交替的红色和黑色结点。因为根据性质5所有最长的路径都有相同数目的黑色结点,这就表明了没有路径能多于任何其他路径的两倍长。
因为红黑树是一种特化的二叉查找树,所以红黑树上的只读操作与普通二叉查找树相同。

节点

template <class K, class V>
struct RBNode

	//typedef bool color;

	RBNode<K, V>* _parent;
	RBNode<K, V>* _left;
	RBNode<K, V>* _right;

	pair<K, V> _kv;
	//颜色
	COLOR _color;

	RBNode(const pair<K, V>& kv = pair<K, V>())
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _kv(kv)
		, _color(RED)
	
;

红黑树的实现

template <class K, class V>
class RBTree

public:
	typedef RBNode<K, V> Node;

	RBTree()
		:_header(new Node)
	
		//创建空树
		_header->_left = _header->_right = _header;
	

	bool insert(const pair<K, V>& kv)
	
		//1. 搜索树的插入
		//空树: _header->parent: nullptr
		if (_header->_parent == nullptr)
		
			//创建根节点
			Node* root = new Node(kv);

			_header->_parent = root;
			root->_parent = _header;
			_header->_left = _header->_right = root;

			//根节点是黑色
			root->_color = BLACK;
			return true;
		

		//从根节点开始搜索
		Node* cur = _header->_parent;
		Node* parent = nullptr;
		while (cur)
		
			parent = cur;
			//和key值进行比较
			if (cur->_kv.first == kv.first)
			
				//key值不允许重复
				return false;
			
			else if (cur->_kv.first > kv.first)
			
				cur = cur->_left;
			
			else
			
				cur = cur->_right;
			
		

		//创建待插入的节点
		cur = new Node(kv);
		if (parent->_kv.first > cur->_kv.first)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//2. 修改颜色或调整结构
		//判断是否有红色连续的节点
		while (cur != _header->_parent && cur->_parent->_color == RED)
		
			parent = cur->_parent;
			Node* gfather = parent->_parent;

			if (gfather->_left == parent)
			
				Node* uncle = gfather->_right;
				//1.uncle是存在的,并且是红色的
				if (uncle && uncle->_color == RED)
				
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//继续更新
					cur = gfather;
				
				else
				
					//cout << "Rotate" << endl;
					//判断是否为双旋的场景
					if (cur == parent->_right)
					
						//左旋
						RotateL(parent);
						//交换cur, parent指向,退化成右旋的场景
						swap(cur, parent);
					
					//右旋
					RotateR(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				
			
			else
			
				Node* uncle = gfather->_left;
				if (uncle && uncle->_color == RED)
				
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					cur = gfather;
				
				else
				
					//cout << "Rotate" << endl;
					if (cur == parent->_left)
					
						RotateR(parent);
						swap(cur, parent);
					
					RotateL(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				
			
		
		//根节点的颜色改成黑色
		_header->_parent->_color = BLACK;
		//更新header的左右指向
		_header->_left = leftMost();
		_header->_right = rightMost();
		return true;
	

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

		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		//判断根
		if (parent == _header->_parent)
		
			_header->_parent = subR;
			subR->_parent = _header;
		
		else
		
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
			subR->_parent = pparent;
		
		parent->_parent = subR;
	

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

		subL->_right = parent;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//判断根
		if (parent == _header->_parent)
		
			_header->_parent = subL;
			subL->_parent = _header;
		
		else
		
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		
		parent->_parent = subL;
	

	Node* leftMost()
	
		Node* cur = _header->_parent;
		while (cur && cur->_left)
		
			cur = cur->_left;
		
		return cur;
	

	Node* rightMost()
	
		Node* cur = _header->_parent;
		while (cur && cur->_right)
		
			cur = cur->_right;
		
		return cur;
	

	void inorder()
	
		_inorder(_header->_parent);
		cout << endl;
	

	void _inorder(Node* root)
	
		if (root)
		
			_inorder(root->_left);
			cout << root->_kv.first << " ";
			_inorder(root->_right);
		
	

	//红黑树
	//1. 根: 黑色
	//2. 每条路径黑色个数相同
	//3. 红色不能连续
	bool isbalance()
	
		if (_header->_parent == nullptr)
			return true;
		Node* root = _header->_parent;
		if (root->_color == RED)
			return false;
		//统计一条路径上的黑色节点个数
		int bCount = 0;
		Node* cur = root;
		while (cur)
		
			if (cur->_color == BLACK)
				++bCount;
			cur = cur->_left;
		
		//遍历每一条路径
		int curBCount = 0;
		return _isblance(root, bCount, curBCount);
	

	bool _isblance(Node* root, int& bCount, int curBCount)
	
		//当root为空时,一条路径遍历结束
		if (root == nullptr)
		
			//判断黑色节点个数是否相同
			if (curBCount != bCount)
				return false;
			else
				return true;
		
		//判断节点是否为黑色
		if (root->_color == BLACK)
			++curBCount;

		//判断是否有红色连续节点
		if (root->_parent && root->_color == RED && root->_parent->_color == RED)
		
			cout << "data: " << root->_kv.first << endl;
			return false;
		
		return _isblance(root->_left, bCount, curBCount) 
		&& _isblance(root->_right, bCount, curBCount);
	

	//成员: header
private:
	Node* _header;
;

以上是关于初识C++之红黑树的主要内容,如果未能解决你的问题,请参考以下文章

C++从入门到入土第二十二篇:数据结构之红黑树

C++从入门到入土第二十二篇:数据结构之红黑树

C++从入门到入土第二十二篇:数据结构之红黑树

C++从入门到入土第二十二篇:数据结构之红黑树

算法之红黑树

算法之红黑树