红黑树实现

  • 红黑树的概念
  • 红黑树的插入调整
  • 判断是否为红黑树
  • 红黑树的性能

一.红黑树的概念

1.红黑树的定义

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

2.红黑树的性质
  • 每个节点不是红色就是黑色
  • 根节点必为黑色
  • 如果一个节点的红色,则它的左右节点都是黑色(不能出现连续的红色节点)
  • 每条路径上的黑色节点数量相同
  • 将空节点视为黑色节点(为了更清晰的表示路径)

只要满足上述条件,一定可以保证最长路径不超过最短路径的两倍。因为每条路径的黑色节点相同,所以在一棵树中,最短路径一定是全黑,最长路径一定是一黑一红,黑红相间。

3.红黑树的结构
#include<iostream>
using namespace std;

namespace zs
{
	enum Colour
	{
		RED,
		BLACK
	};
	template<class K, class V>
	struct RBTreeNode
	{
		RBTreeNode<K, V>* _left;
		RBTreeNode<K, V>* _right;
		RBTreeNode<K, V>* _parent;

		Colour _col;
		pair<K, V> _kv;
		RBTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_kv(kv)
			,_col(RED)    
	//默认插入颜色设置为红色,因为默认为RED,可能会破坏规则,但默认为黑色一定会破坏规则
		{}
	};
	template<class K, class V>
	class RBTree
	{
		typedef RBTreeNode<K, V> node;
	public:
        // .....功能
	private:
		node* _root = nullptr;
	};
}

二.红黑树的插入调整

1.插入节点是根节点(违反了规则,需要调整)

将根节点颜色调整为黑色即可

2.插入节点的双亲节点是黑色

该红黑树不需要继续调整

3.插入节点的双亲是红色(违反了规则,需要调整)

由于插入节点是红色,插入节点的双亲节点是红色,所以必有祖父节点且为黑色。不管cur和parent是怎样的位置关系,grandfather永远为黑色,parent永远为红色,cur永远为红色。
image.png

3.1叔叔节点存在且为红色(变色即可)

此种情况的抽象图:
image.png
A,B,C,D,E为一颗树
此时,只需要将paren变为黑色,uncle变为黑色,grandfather变为红色,这颗树已经调整为红黑树了,但是是否继续更新,取决于grandfather的parent是否为红色或者grandfather是否为根。
image.png

  • 如果grandfather的parent是黑色,则停止更新
  • 如果grandfather是根节点,则将它变为黑色,停止更新
3.2叔叔节点不存在或者存在且为黑色(旋转+变色)

抽象图:
image.png
如果叔叔节点不存在/存在且为黑色,此时仅仅改变节点的颜色,已经无法保证满足红黑树的规则了,所以需要通过旋转来改变树形,然后再改变节点颜色即可。
根据parent和cur的位置不同,可分为四种旋转方式:
左旋:
image.png
左右旋:
image.png
右旋,右左旋类似。

		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 != nullptr)
			{
				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);
			if (parent->_kv.first < kv.first)
			{
				parent->_right = cur;
	
			}
			else
			{
				parent->_left = cur;
	
			}
			cur->_parent = parent;

			//进入红黑树部分
			//当parent不存在/parent为黑色,则说明不需要向上更新
			while (parent != nullptr && parent->_col == RED)
			{
				//注意这里的grandfather必不可能为nullptr,因为当parent=RED,cur=RED,其grandfather必为黑
				node* grandfather = parent->_parent;
				//判读parnet是grandparent的左还是右,为了找uncle
				if (parent == grandfather->_left)
				{
					node* uncle = grandfather->_right;

					//情况1:uncle存在且为红
					if (uncle != nullptr && uncle->_col == RED)
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						//因为grandfather=RED,可能会出现连续的RR,所以应该继续更新
						cur = grandfather;
						parent = cur->_parent;
					}
					else //情况2+情况3:uncle不存在/存在且为黑,需要旋转+变色
					{
						//如果cur是parent->left,说明需要右单旋
						if (cur == parent->_left)
						{
							RotateR(grandfather);

							//修改颜色
							parent->_col = BLACK;
							grandfather->_col = RED;

						}
						else//如果cur是parent->right,说明需要左右双旋
						{
							RotateL(parent);
							RotateR(grandfather);

							//更新节点颜色
							cur->_col = BLACK;
							parent->_col = RED;
							grandfather->_col = RED;
						}

						//旋转后,子树的根为黑色,不会影响上层,退出跟新
						break;
					}
				}
				else
				{
					node* uncle = grandfather->_left;

					//情况1:uncle存在且为红
					if (uncle != nullptr && uncle->_col == RED)
					{
						parent->_col = BLACK;
						uncle->_col = BLACK;
						grandfather->_col = RED;

						//因为grandfather=RED,可能会出现连续的RR,所以应该继续更新
						cur = grandfather;
						parent = cur->_parent;
					}
					else //情况2+情况3:uncle不存在/存在且为黑,需要旋转+变色
					{
						//如果cur是parent->right,说明需要左单旋
						if (cur == parent->_right)
						{
							RotateL(grandfather);

							//修改颜色
							parent->_col = BLACK;
							grandfather->_col = RED;

						}
						else//如果cur是parent->left,说明需要右左双旋
						{
							RotateR(parent);
							RotateL(grandfather);

							//更新节点颜色
							cur->_col = BLACK;
							parent->_col = RED;
							grandfather->_col = RED;
						}

						//旋转后,子树的根为黑色,不会影响上层,退出跟新
						break;
					}
				
				}

				//最后需要把根变为黑色,因为有可能因为parent不存在而停止更新,
				
			}
			_root->_col = BLACK;
			return true;

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

			parent->_right = subRL;
			if (subRL != nullptr)
			{
				subRL->_parent = parent;
			}

			node* pparent = parent->_parent;

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

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

			}
			else
			{
				_root = subR;
				subR->_parent = nullptr;
			}
		}
		void RotateR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;

			parent->_left = subLR;
			if (subLR != nullptr)
			{
				subLR->_parent = parent;
			}

			node* pparent = parent->_parent;
			subL->_right = parent;
			parent->_parent = subL;



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

三.判断是否为红黑树

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质
		bool isBalance()
		{
			if (_root == nullptr)
			{
				return true;
			}

			//如果根存在且根为红色,则不是RBT
			if (_root && _root->_col == RED)
			{
				cout << "根不是黑色" << endl;
				return false;
			}

			//检查红色节点的子节点是黑色,同时检查每条路径的黑色节点是否相同
			int baseValue = -1;
			return _check(_root, 0, baseValue);
		}
		bool _check(node* root, int blackNum, int& baseValue)
		{
			if (root == nullptr)
			{
				if (baseValue == -1)
				{
					baseValue = blackNum;

				}
				else
				{
					if (baseValue != blackNum)
					{
						cout << "某条路径的黑色节点数量不同" << endl;
						return false;
					}
				}
				return true;
			}

			//如果根为红色说明该节点必有父亲节点
			if (root->_col == RED && root->_parent->_col == RED)
			{
				cout << "出现连续的红色节点" << endl;
				return false;
			}
			if (root->_col == BLACK)
			{
				blackNum++;
			}

			return _check(root->_left, blackNum, baseValue)
				&& _check(root->_right, blackNum, baseValue);
		}

四.红黑树的性能

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

  • 红黑树的应用场景:
  1. C++ STL库 – map/set、mutil_map/mutil_set
  2. Java 库
  3. linux内核
  4. 其他一些库
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值