【C++】红黑树详解

本文是小编巩固自身而作,如有错误,欢迎指出!


目录

一、红黑树的概念

二、红黑树的基本规则

三、红黑树的实现

3.1红黑树的基本结构

3.2红黑树的调整方法

(1)parent为黑

(2)uncle为红

(3)uncle不存在,或者uncle为黑色

(4)uncle不存在或为黑,且c不在g和p的一条线上

3.3红黑树平衡检测等功能


一、红黑树的概念

红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束**,红⿊树确保没有⼀条路径会⽐其他路 径⻓出2倍**,因⽽是接近平衡的。

二、红黑树的基本规则

1. 每个结点不是红色就是黑色

2. 根结点是黑色的

3. 如果⼀个结点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意⼀条路径不会有连续的 红色结点

4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点

根据上述规则,也就实现了我们提到的红黑树的关键概念红⿊树确保没有⼀条路径会⽐其他路 径⻓出2倍

三、红黑树的实现

3.1红黑树的基本结构

和普通的key/value二叉树相比,红黑树在结构上会多出一个颜色选项。

cpp 复制代码
// 枚举值表⽰颜⾊ 
enum Colour
{
 RED,
 BLACK
};
// 这⾥我们默认按key/value结构实现 
template<class K, class V>
struct RBTreeNode
{
 // 这⾥更新控制平衡也要加⼊parent指针 
 pair<K, V> _kv;
 RBTreeNode<K, V>* _left;
 RBTreeNode<K, V>* _right;
 RBTreeNode<K, V>* _parent;
 Colour _col;
 RBTreeNode(const pair<K, V>& kv)
 :_kv(kv)
 , _left(nullptr)
 , _right(nullptr)
 , _parent(nullptr)
 {}
};
template<class K, class V>
class RBTree
{
 typedef RBTreeNode<K, V> Node;
public:
private:
 Node* _root = nullptr;
};

3.2红黑树的调整方法

通过前面的四大规则我们就可以知道,如果要对红黑树调整,一定是一个复杂的过程,不仅要考虑两边的高度,还要考虑节点的颜色。

我们首先思考一下,红黑树新插入的节点应该是什么颜色**?**

很显然,为了符合上述的四大规则,只能是红色。为什么呢?

因为,插入黑色必然会导致当前路径上的黑色节点数量和其他路径不同了。(插入都是找找到NULL位置插入)

但是如果在红色节点后插入红色节点,不也违反规则三、连续出现了红色节点了吗?因此,我们在插入后,就涉及到一个新的问题------变色

下面我们一个个分析,不同情况下的调整方法。

首先,我们导入一个概念

下图中假设我们把新增结点标识为c(cur),c的⽗亲标识为p(parent),p的⽗亲标识为 g(grandfather),p的兄弟标识为u(uncle)

(1)parent为黑

当parent为黑色时,不用做额外的调整,直接插入即可。

(2)uncle为红

在上图所示的情况下我们只进行变色操作就可以了。

  1. P变黑
  2. G变红
  3. U变黑
cpp 复制代码
         
				Node* uncle = grandfather->_right;
				//叔叔存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}

(3)uncle不存在,或者uncle为黑色

当parent和uncle都存在且不同色的时候,我们发现,如果只是简单的变色已经不足以满足上述规则了,因此我们在这一类情况的时候,我们采用的方法是旋转+变色

  1. P变黑
  2. G变红
  3. 单旋转

而关于旋转,我们之前在AVL树的章节进行了详细解释,对其不熟悉的同学可以看看以下链接

AVL树详解https://blog.csdn.net/2401_85487070/article/details/152172361?fromshare=blogdetail&sharetype=blogdetail&sharerId=152172361&sharerefer=PC&sharesource=2401_85487070&sharefrom=from_link

cpp 复制代码
else//叔叔不存在或叔叔为黑,旋转+变色
				{
					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;
				}

(4)uncle不存在或为黑,且c不在g和p的一条线上

就如同我们之前学习的AVL树的双旋情况相似,红黑树与其最大的区别就在于红黑树在调整完一层后,因为颜色的变化需要继续向上不断调整。

cpp 复制代码
else//grandfather->_right==parent
			{
				//   g
				// u   p
				Node* uncle = grandfather->_left;
				// 叔叔存在且为红,变色即可
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

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

					break;
				}
			}

3.3红黑树平衡检测等功能

下面给大家带来计数,求高,验证平衡的函数,原理和先前的AVL树相似,不过多赘述

求节点数

cpp 复制代码
int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}

求高度

cpp 复制代码
int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

验证是否平衡

cpp 复制代码
bool IsBalance()
	 {
		 if (_root == nullptr)
			 return true;

		 if (_root->_col == RED)
			 return false;

		 // 黑色节点数量参考值
		 Node* leftMost = _root;
		 int blackRef = 0;
		 while (leftMost)
		 {
			 if (leftMost->_col == BLACK)
				 ++blackRef;

			 leftMost = leftMost->_left;
		 }

		 return Check(_root, 0, blackRef);
	 }
bool Check(Node* cur, int blackNum, const int blackNumRef)
	{
		if (cur == nullptr)
		{
			if (blackNum != blackNumRef)
			{
				cout << "黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
		{
			cout << cur->_kv.first << "->" << "连续的红色节点" << endl;
			return false;
		}

		if (cur->_col == BLACK)
			++blackNum;

		return Check(cur->_left, blackNum, blackNumRef)
			&& Check(cur->_right, blackNum, blackNumRef);
	}

四、完整代码及其测试

.h文件

cpp 复制代码
#pragma once
#include<iostream>
using namespace std;

enum Colour
{
	RED, BLACK
};

template<class K,class V>
struct BRTreeNode
{
	pair<K, V> _kv;
	BRTreeNode<K, V>* _left;
	BRTreeNode<K, V>* _right;
	BRTreeNode<K, V>* _parent;
	Colour _col;
	BRTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
	{

	}

};


template<class K, class V>
class BRTree
{
	typedef BRTreeNode<K, V> Node;
public:
	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;
		}
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (!grandfather) break; // 如果grandfather为空,退出循环
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				//叔叔存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
			
				else//叔叔不存在或叔叔为黑,旋转+变色
				{
					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//grandfather->_right==parent
			{
				//   g
				// u   p
				Node* uncle = grandfather->_left;
				// 叔叔存在且为红,变色即可
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 叔叔不存在,或者存在且为黑
				{
					// 情况二:叔叔不存在或者存在且为黑
					// 旋转+变色
					//   g
					// u   p
					//       c
					if (cur == parent->_right)
					{
						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;
	 }
	 void InOrder()
	 {
		 _InOrder(_root);

	 }
	 //bool IsBalance() {
		// if (_root == nullptr) return true;
		// if (_root->_col == RED) return false;

		// // 计算参考值(最左路径黑节点数)
		// Node* leftMost = _root;
		// int blackRef = 0;
		// while (leftMost) {
		//	 if (leftMost->_col == BLACK) ++blackRef;
		//	 leftMost = leftMost->_left;
		// }
		// return Check(_root, 0, blackRef);
	 //}
	 bool IsBalance()
	 {
		 if (_root == nullptr)
			 return true;

		 if (_root->_col == RED)
			 return false;

		 // 黑色节点数量参考值
		 Node* leftMost = _root;
		 int blackRef = 0;
		 while (leftMost)
		 {
			 if (leftMost->_col == BLACK)
				 ++blackRef;

			 leftMost = leftMost->_left;
		 }

		 return Check(_root, 0, blackRef);
	 }
	 
	 int Height()
	 {
		 return _Height(_root);
	 }

	 int Size()
	 {
		 return _Size(_root);
	 }

	 Node* Find(const K& key)
	 {
		 Node* cur = _root;
		 while (cur)
		 {
			 if (cur->_kv.first < key)
			 {
				 cur = cur->_right;
			 }
			 else if (cur->_kv.first > key)
			 {
				 cur = cur->_left;
			 }
			 else
			 {
				 return cur;
			 }
		 }
		 return nullptr;
	 }


private:
	//bool Check(Node* cur, int blackNum, const int blackRef) {
	//	if (cur == nullptr)
	//		return blackNum == blackRef;  // 修正1:正确判断路径终点

	//	// 修正2:避免根节点访问_parent
	//	if (cur != _root && cur->_col == RED && cur->_parent->_col == RED) {
	//		cout << "连续红节点:" << cur->_kv.first << endl;
	//		return false;
	//	}

	//	// 更新黑节点计数
	//	int newBlackNum = (cur->_col == BLACK) ? blackNum + 1 : blackNum;

	//	// 修正3:独立传递左右子树计数
	//	int leftCount = newBlackNum;
	//	int rightCount = newBlackNum;
	//	bool leftValid = Check(cur->_left, leftCount, blackRef);
	//	bool rightValid = Check(cur->_right, rightCount, blackRef);

	//	return leftValid && rightValid;
	//}
	//	 void _InOrder(Node* root)
	//	 {
	//		 if (root == nullptr)
	//			 return;
	//		 _InOrder(root->_left);
	//		 cout << root->_kv.first<<" ";
	//		 _InOrder(root->_right);
	//	 }


	int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	bool Check(Node* cur, int blackNum, const int blackNumRef)
	{
		if (cur == nullptr)
		{
			if (blackNum != blackNumRef)
			{
				cout << "黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
		{
			cout << cur->_kv.first << "->" << "连续的红色节点" << endl;
			return false;
		}

		if (cur->_col == BLACK)
			++blackNum;

		return Check(cur->_left, blackNum, blackNumRef)
			&& Check(cur->_right, blackNum, blackNumRef);
	}
		 void RotateR(Node* parent)
		 {
			 Node* subL = parent->_left;
			 Node* subLR = subL->_right;

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

			 Node* parentParent = parent->_parent;

			 subL->_right = parent;
			 parent->_parent = subL;

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

				 subL->_parent = parentParent;
			 }
		 }

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

			 parent->_right = subRL;
			 if (subRL)
				 subRL->_parent = parent;

			 Node* parentParent = parent->_parent;

			 subR->_left = parent;
			 parent->_parent = subR;

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

				 subR->_parent = parentParent;
			 }
		 }

		Node* _root = nullptr;

	};

测试文件

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"BRTree.h"
#include<vector>


void TestBRTree1()
{
	BRTree<int, int> t;
	// 常规的测试用例
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的带有双旋场景的测试用例
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : a)
	{
		if (e == 14)
		{
			int x = 0;
		}

		t.Insert({ e, e });
		//t.InOrder();
		//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
		cout << t.IsBalance();
	}
	//t.InOrder();

}
void TestBRTree2()
{
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	size_t begin2 = clock();
	BRTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();
	cout << "Insert:" << end2 - begin2 << endl;
	cout << t.IsBalance() << endl;
	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	/*for (auto e : v)
	{
	t.Find(e);
	}*/
	// 随机值
	for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}
	size_t end1 = clock();

	cout << "Find:" << end1 - begin1 << endl;
}

int main()
{
	TestBRTree2();
}

本次分享就到这里结束了,感谢阅读!

相关推荐
时间醉酒3 小时前
数据结构实战:顺序表全解析 - 从零实现到性能分析
数据结构
Craaaayon3 小时前
【数据结构】二叉树-图解广度优先搜索
java·数据结构·后端·算法·宽度优先
星哥说事3 小时前
Python自学25 - Django快速上手
开发语言·python·django
斑点鱼 SpotFish4 小时前
用Python可视化国庆期间旅游概况与消费趋势
开发语言·python·旅游
only-lucky4 小时前
在Qt中使用VTK
开发语言·qt
刘某某.4 小时前
数组和小于等于k的最长子数组长度b
java·数据结构·算法
小杰帅气4 小时前
类与对象1
开发语言·c++
chenyuhao20245 小时前
《C++二叉引擎:STL风格搜索树实现与算法优化》
开发语言·数据结构·c++·后端·算法
杂亿稿5 小时前
优先级队列
数据结构