红黑树

17 篇文章 0 订阅
11 篇文章 0 订阅

目录

1.红黑树的概念

2.红黑树的性质

3.红黑树的操作

3.1 节点定义

3.2 红黑树实现结构

3.3 find操作

3.4 insert操作

3.5 erase操作

3.6 红黑树的检验

4. 红黑树与AVL树的比较

5.模拟实现代码


1.红黑树的概念

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

2.红黑树的性质

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

确保最长路径不超过最短路径的二倍,即最短路径 < 最长路径 <= 最短路径*2 原理(不考虑叶子节点->空节点):
理论最短路径->全黑,实际最短路径 >= 理论最短路径
理论最长路径->一红一黑交替(红结束),实际最长路径<=理论最长路径
由条件4得知从任意节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点,
则有理论最长路径 == 理论最短路径*2, 所以有实际最长路径 <= 理论最长路径 == 理论最短路径 *2 <= 实际最短路径 *2 ,即实际最长路径 <= 实际最短路径 *2

3.红黑树的操作

3.1 节点定义

三叉链结构,_data指向值域,_color标记节点的颜色

enum Color
{
	_red, _black//0--red, 1--black
};
template<class K, class V>
struct RBTreeNode
{
	typedef RBTreeNode<K, V> Node;
	//init
	//color默认给_red
	RBTreeNode(const pair<K, V>& data = pair<K, V>(), Color color = _red)
		:_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
	{}
	Node* _left;
	Node* _right;
	Node* _parent;
	pair<K, V> _data;
	Color _color;
};

3.2 红黑树实现结构

红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的_parent 域指向红黑树的根节点,_left 域指向红黑树中最小的节点_right 域指向红黑树中最大的节点。

3.3 find操作

根据查找的key值大小,大于key向右查找,小于key向左查找,走到nullptr 表示不存在

Node* find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (key > cur->_data.first)
			cur = cur->_right;
		else if (key < cur->_data.first)
			cur = cur->_left;
		else//找到哩
			return cur;
	}
	//cout << "key不存在" << endl;
	return nullptr;
}

3.4 insert操作

1.查找插入位置
2.插入节点
3.调整恢复红黑树结构(检测新节点插入后,红黑树的性质是否造成破坏):

新节点的默认颜色是红色,如果如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
(约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)

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

将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

b. cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的左孩子,则进行右单旋转;
p为g的右孩子,cur为p的右孩子,则进行左单旋转;
p、g变色--p变黑,g变红

c. cur为红,p为红,g为黑,u不存在/u存在且为黑

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,再对g做右单旋转(情况b)
p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,再对g做左单旋转(情况b)

bool insert(const pair<K, V>& data)
{
	//1.查找插入位置
	//2.插入节点
	//3.调整RBT
	// 
	//1.查找插入位置
	//空树直接插入--根黑
	if (_root == nullptr)
	{
		_root = new Node(data,_black);//根黑
		_root->_parent = _head;
		_head->_parent = _root;
		_head->_left = _head->_right = _root;
		return true;
	}
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		if (data.first > cur->_data.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (data.first < cur->_data.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else//key已存在
		{
			return  false;
		}
	}
	//2.插入节点--除根节点外,默认插入节点为红色
	cur = new Node(data, _red);
	if (parent->_data.first > data.first)
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	//3.调整
	//3.1插入后无连续红节点--直接插入,无需修改
	//3.2插入后有连续红节点--需要调整,调整需要看parent的兄弟节点uncle
	while (parent && parent->_color == _red)
	{
		//parent为红说明一定存在parent的父节点
		Node* gfather = parent->_parent;
		//     g
		//p        u
		if (parent == gfather->_left)
		{
			Node* uncle = gfather->_right;
			//uncle存在且为红
			if (uncle && uncle->_color == _red)
			{
				parent->_color = _black;
				uncle->_color = _black;
				gfather->_color = _red;

				//需要继续更新,上层可能因为gfather变红出现连续红节点
				cur = gfather;
				parent = gfather->_parent;
			}
			else//uncle存在且为黑或不存在 -> 旋转+变色
			{
				if (cur == parent->_left) // LL型
				{
					//       g
					//   p        u
					//c
					//右旋
					RotateR(gfather);
					gfather->_color = _red;
					parent->_color = _black;
					//根为黑,不需要向上更新了
				}
				else//LR型
				{
					//     g
					// p      u
					//     c
					//左旋+右旋
					RotateL(parent);
					RotateR(gfather);
					
					cur->_color = _black;
					gfather->_color = _red;
					//根为黑,不需要向上更新了
				}
				break;
			}
		}
		//   g
		//u    p
		else//parent在gfather的右边
		{
			Node* uncle = gfather->_left;
			//uncle存在且为红
			if (uncle && uncle->_color == _red)
			{
				parent->_color = uncle->_color = _black;
				gfather->_color = _red;
				//向上更新
				cur = gfather;
				parent = cur->_parent;
			}
			else//叔叔不存在或存在且为黑
			{
				//     g
				//u        p
				//             c
				//RR型
				if (cur == parent->_right)
				{
					RotateL(gfather);
					parent->_color = _black;
					gfather->_color = _red;
				}
				else
				{
					//      g
					//u          p
					//      c
					//RL型
					RotateR(parent);
					RotateL(gfather);
					cur->_color = _black;
					gfather->_color = _red;
				}
				break;
			}
		}
	}
	//4.更新_head的状态
	_root->_color = _black;//根可能会变红,需要变为黑色,全部路径黑节点数+1
	_head->_parent = _root;
	_head->_left = _Min(_root);
	_head->_right = _Max(_root);
	
	return true;
}

//左旋
void RotateL(Node* parent)
{
	Node* p_r = parent->_right;
	Node* p_r_l = p_r->_left;
	
	parent->_right = p_r_l;
	if (p_r_l != nullptr)
		p_r_l->_parent = parent;

	Node* parent_parent = parent->_parent;

	p_r->_left = parent;
	parent->_parent = p_r;

	//p_r变成根
	if (parent_parent == _head)
	{
		_root = p_r;
		p_r->_parent = _head;
		_head->_parent = _root;
		_head->_left = _Min(_root);
		_head->_right = _Max(_root);
	}
	else
	{
		if (parent == parent_parent->_left)
			parent_parent->_left = p_r;
		else
			parent_parent->_right = p_r;

		p_r->_parent = parent_parent;
	}
}
//右旋
void RotateR(Node* parent)
{
	Node* p_l = parent->_left;
	Node* p_l_r = p_l->_right;
	
	parent->_left = p_l_r;
	if (p_l_r != nullptr)
		p_l_r->_parent = parent;

	Node* parent_parent = parent->_parent;
	
	p_l->_right = parent;
	parent->_parent = p_l;

	//变成根节点
	if (parent_parent == _head)
	{
		_root = p_l;
		p_l->_parent = _head;
		_head->_parent = _root;
		_head->_left = _Min(_root);
		_head->_right = _Max(_root);
	}
	else
	{
		if (parent == parent_parent->_left)
			parent_parent->_left = p_l;
		else
			parent_parent->_right = p_l;

		p_l->_parent = parent_parent;
	}
}

3.5 erase操作

参考视频: 
https://www.bilibili.com/video/BV16m421u7Tb?vd_source=c744ec928a14e81c8bf974e8d2d7e80f

3.6 红黑树的检验

1. 检验是否满足二叉搜索树(中序遍历是否是有序序列)
2.检测其是否满足红黑树的性质:不能出现连续红节点,计算每条路径黑节点数量是否相同,根是否为黑

	//检测是否是红黑树
	bool IsRBTree()
	{
		if (_root == nullptr)
			return true;
		if (_root->_color == _red)
			return false;
		
		//计算任意一条路径的黑节点数量
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_color == _black)
				++refNum;
			cur = cur->_left;
		}
		return _Check(_root, 0, refNum);
	}

bool _Check(Node* root, int blackNum, const int refNum)
{
	if (root == nullptr)
	{
		if (refNum != blackNum)
		{
			cout << "存在黑色节点的数量不相等的路径" << endl;
			return false;
		}
		return true;
	}
	if (root->_color == _red && root->_parent->_color == _red)
	{
		cout << root->_data.first << "存在连续红节点" << endl;
		return false;
	}
	if (root->_color == _black)
		blackNum++;
	
	return _Check(root->_left, blackNum, refNum) && _Check(root->_right, blackNum, refNum);
}

4. 红黑树与AVL树的比较

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

5.模拟实现代码

#pragma once
#include <iostream>
#include <time.h>
#include <assert.h>
#include <string>
#include <vector>
#include <map>
#include <cstdlib>
#include <set>
#include <list>
#include <algorithm>
using namespace std;


namespace myRBTree//KV模型
{
	enum Color
	{
		_red, _black//0--red, 1--black
	};
	template<class K, class V>
	struct RBTreeNode
	{
		typedef RBTreeNode<K, V> Node;
		//init
		//color默认给_red
		RBTreeNode(const pair<K, V>& data = pair<K, V>(), Color color = _red)
			:_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _color(color)
		{}
		Node* _left;
		Node* _right;
		Node* _parent;
		pair<K, V> _data;
		Color _color;
	};
	
	template <class K, class V>
	class RBTree
	{
		typedef RBTreeNode<K, V> Node;
	public:
		RBTree()
			:_root(nullptr)
		{
			_head = new Node(pair<K, V>{INT_MIN, INT_MAX}, _black);
		}
		RBTree(const RBTree<K,V>& tree)
		{
			_head = new Node(pair<K, V>{INT_MIN, INT_MAX}, _black);
			_root = _Copy(tree._root, tree._root->_parent);
			_root->_parent = _head;
			//哨兵头节点链接
			_head->_parent = _root;
			_head->_left = _Min(_root);
			_head->_right = _Max(_root);
		}
		RBTree<K, V>& operator=(RBTree<K, V> tree)
		{
			//现代写法
			std::swap(_root, tree._root);
			std::swap(_head,tree._head);
			//局部变量自动销毁
			return *this;
		}
		~RBTree()
		{
			_destroy(_root);
			delete _head;
			_root = nullptr;
			_head = nullptr;
		}
		Node* find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key > cur->_data.first)
					cur = cur->_right;
				else if (key < cur->_data.first)
					cur = cur->_left;
				else//找到哩
					return cur;
			}
			//cout << "key不存在" << endl;
			return nullptr;
		}
		bool insert(const pair<K, V>& data)
		{
			//1.查找插入位置
			//2.插入节点
			//3.调整RBT
			// 
			//1.查找插入位置
			//空树直接插入--根黑
			if (_root == nullptr)
			{
				_root = new Node(data,_black);//根黑
				_root->_parent = _head;
				_head->_parent = _root;
				_head->_left = _head->_right = _root;
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (data.first > cur->_data.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (data.first < cur->_data.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else//key已存在
				{
					return  false;
				}
			}
			//2.插入节点--除根节点外,默认插入节点为红色
			cur = new Node(data, _red);
			if (parent->_data.first > data.first)
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else
			{
				parent->_right = cur;
				cur->_parent = parent;
			}
			//3.调整
			//3.1插入后无连续红节点--直接插入,无需修改
			//3.2插入后有连续红节点--需要调整,调整需要看parent的兄弟节点uncle
			while (parent && parent->_color == _red)
			{
				//parent为红说明一定存在parent的父节点
				Node* gfather = parent->_parent;
				//     g
				//p        u
				if (parent == gfather->_left)
				{
					Node* uncle = gfather->_right;
					//uncle存在且为红
					if (uncle && uncle->_color == _red)
					{
						parent->_color = _black;
						uncle->_color = _black;
						gfather->_color = _red;

						//需要继续更新,上层可能因为gfather变红出现连续红节点
						cur = gfather;
						parent = gfather->_parent;
					}
					else//uncle存在且为黑或不存在 -> 旋转+变色
					{
						if (cur == parent->_left) // LL型
						{
							//       g
							//   p        u
							//c
							//右旋
							RotateR(gfather);
							gfather->_color = _red;
							parent->_color = _black;
							//根为黑,不需要向上更新了
						}
						else//LR型
						{
							//     g
							// p      u
							//     c
							//左旋+右旋
							RotateL(parent);
							RotateR(gfather);
							
							cur->_color = _black;
							gfather->_color = _red;
							//根为黑,不需要向上更新了
						}
						break;
					}
				}
				//   g
				//u    p
				else//parent在gfather的右边
				{
					Node* uncle = gfather->_left;
					//uncle存在且为红
					if (uncle && uncle->_color == _red)
					{
						parent->_color = uncle->_color = _black;
						gfather->_color = _red;
						//向上更新
						cur = gfather;
						parent = cur->_parent;
					}
					else//叔叔不存在或存在且为黑
					{
						//     g
						//u        p
						//             c
						//RR型
						if (cur == parent->_right)
						{
							RotateL(gfather);
							parent->_color = _black;
							gfather->_color = _red;
						}
						else
						{
							//      g
							//u          p
							//      c
							//RL型
							RotateR(parent);
							RotateL(gfather);
							cur->_color = _black;
							gfather->_color = _red;
						}
						break;
					}
				}
			}
			//4.更新_head的状态
			_root->_color = _black;//根可能会变红,需要变为黑色,全部路径黑节点数+1
			_head->_parent = _root;
			_head->_left = _Min(_root);
			_head->_right = _Max(_root);
			
			return true;
		}
		void InOrder()
		{
			_InOrder(_root);
		}
		//检测是否是红黑树
		bool IsRBTree()
		{
			if (_root == nullptr)
				return true;
			if (_root->_color == _red)
				return false;
			
			//计算任意一条路径的黑节点数量
			int refNum = 0;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_color == _black)
					++refNum;
				cur = cur->_left;
			}
			return _Check(_root, 0, refNum);
		}
		int size()
		{
			return _size(_root);
		}
		
	private:
		int _size(Node* root)
		{
			if (root == nullptr)
				return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}
		bool _Check(Node* root, int blackNum, const int refNum)
		{
			if (root == nullptr)
			{
				if (refNum != blackNum)
				{
					cout << "存在黑色节点的数量不相等的路径" << endl;
					return false;
				}
				return true;
			}
			if (root->_color == _red && root->_parent->_color == _red)
			{
				cout << root->_data.first << "存在连续红节点" << endl;
				return false;
			}
			if (root->_color == _black)
				blackNum++;
			
			return _Check(root->_left, blackNum, refNum) && _Check(root->_right, blackNum, refNum);
		}

		void _InOrder(Node* root)
		{
			if (root == nullptr)
				return;
			_InOrder(root->_left);
			cout << root->_data.first << ':' << root->_data.second << ' ' << root->_color << endl;
			//cout << root->_data.first << ':' << root->_data.second << endl;
			_InOrder(root->_right);
		}
		int _Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			int heightL = _Height(root->_left);
			int heightR = _Height(root->_right);
			
			return max(heightL, heightR) + 1;
		}
		//左旋
		void RotateL(Node* parent)
		{
			Node* p_r = parent->_right;
			Node* p_r_l = p_r->_left;
			
			parent->_right = p_r_l;
			if (p_r_l != nullptr)
				p_r_l->_parent = parent;

			Node* parent_parent = parent->_parent;

			p_r->_left = parent;
			parent->_parent = p_r;

			//p_r变成根
			if (parent_parent == _head)
			{
				_root = p_r;
				p_r->_parent = _head;
				_head->_parent = _root;
				_head->_left = _Min(_root);
				_head->_right = _Max(_root);
			}
			else
			{
				if (parent == parent_parent->_left)
					parent_parent->_left = p_r;
				else
					parent_parent->_right = p_r;

				p_r->_parent = parent_parent;
			}
		}
		//右旋
		void RotateR(Node* parent)
		{
			Node* p_l = parent->_left;
			Node* p_l_r = p_l->_right;
			
			parent->_left = p_l_r;
			if (p_l_r != nullptr)
				p_l_r->_parent = parent;

			Node* parent_parent = parent->_parent;
			
			p_l->_right = parent;
			parent->_parent = p_l;

			//变成根节点
			if (parent_parent == _head)
			{
				_root = p_l;
				p_l->_parent = _head;
				_head->_parent = _root;
				_head->_left = _Min(_root);
				_head->_right = _Max(_root);
			}
			else
			{
				if (parent == parent_parent->_left)
					parent_parent->_left = p_l;
				else
					parent_parent->_right = p_l;

				p_l->_parent = parent_parent;
			}
		}
		void _destroy(Node* _root)
		{
			if (_root == nullptr)
				return;
			_destroy(_root->_left);
			_destroy(_root->_right);
			delete _root;
		}
		Node* _Copy(Node* root, Node* newRootParent)
		{
			if (root == nullptr)
				return nullptr;
			Node* newRoot = new Node(root->_data,root->_color);
			newRoot->_left = _Copy(root->_left, newRoot);
			newRoot->_right = _Copy(root->_right, newRoot);
			newRoot->_parent = newRootParent;

			return newRoot;
		}
		Node* _Min(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			while (root->_left != nullptr)
			{
				root = root->_left;
			}
			return root;
		}
		Node* _Max(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			while (root->_right != nullptr)
			{
				root = root->_right;
			}
			return root;
		}

	private:
		Node* _root;
		Node* _head;
	};
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

饼干烧饼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值