红黑树 | C++实现

1.红黑树的定义与性质

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

  • 每个结点不是红色就是黑色。
  • 根节点是黑色的。
  • 如果一个节点是红色的,则它的两个孩子结点是黑色的。
  • 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点。
  • 每个叶子结点都是黑色的(此处的叶子结点指的是空结点。

2.节点定义

struct RBTNode
{
	RBTNode(const pair<K,V>& data = pair<K,V>())
		:_parent(nullptr)
		,_right(nullptr)
		,_left(nullptr)
		,color(red)
		,_data(data)
	{}
	pair<K,V> _data;
	RBTNode<K,V>* _parent;
	RBTNode<K,V>* _right;
	RBTNode<K,V>* _left;
	COLOR color;
};

3.如何调整?

3.1 情况一:父节点为红,叔叔节点为红

来看下面这种情况:
插入cur节点时,左侧出现两个连续的红色节点,此时就需要调整,我们发现叔叔节点u存在,所以处理方式是,将父节点与叔叔节点同时变黑,并将祖父节点g变为红色。然后cur交换至g,继续向上调整。
在这里插入图片描述

3.2 情况2:父节点为红,叔叔节点为黑/不存在

此时分为两种情况:
情况a: 父节点为祖父节点的左(右)节点,插入节点cur为父节点的左(右)节点。
来看这种情况:
插入节点为父节点的左孩子,处理方法为:对p进行右旋转,随后将g变为红,p变为黑。
在这里插入图片描述

**情况b:**父节点为祖父节点的左(右)节点,插入节点cur为父节点的右(左)节点。
来看这种情况:
插入节点为父亲的左孩子,处理方法为:
先对父节点p进行做单旋,得到如下结果,此时就变为了情况a,然后使用情况a的方法,即可完成调整。

在这里插入图片描述

bool Insert(const pair<K, V>& data)
	{
		if (_head->_parent == nullptr)
		{
			Node* root = new Node(data);
			_head->_parent = root;
			_head->_left = _head->_right = root;
			root->color = black;
			root->_parent = _head;
			return true;
		}
		Node* cur = _head->_parent;
		Node* parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_data.first == data.first)
			{
				return false;
			}
			if (cur->_data.first > data.first)
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		cur = new Node(data);
		if (parent->_data.first > data.first)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//调整 : cur和parent都为红色,需要调整
		while (cur != _head->_parent && cur->_parent->color == red)
		{
			Node* parent = cur->_parent;
			Node* Gparent = parent->_parent;
			//祖父节点的左孩子存在并且为红色
			if (Gparent->_left == parent)
			{
				Node* uncle = Gparent->_right;
				if (uncle && uncle->color == red)
				{
					uncle->color = parent->color = black;
					Gparent->color = red;
					cur = Gparent;
				}
			         //如果cur = parent->right 左单旋一次  
					 //cur = parent->left 右单旋一次 得到上面的情况 再做单旋一次即可
			    else
				{
					if (cur == parent->_right)
					{
						RotateLeft(parent);
						swap(cur, parent);
					}
					RotateRight(Gparent);
					parent->color = black;
					Gparent->color = red;
					break;
				}
			}
			else
			{
				Node *uncle = Gparent->_left;
				if (uncle && uncle->color == red)
				{
					uncle->color = parent->color = black;
					Gparent->color = red;
					cur = Gparent;
				}
				else
				{
					if (cur == parent->_left)
					{
       					RotateRight(parent);
						swap(cur, parent);
					}
					RotateLeft(Gparent);
					parent->color = black;
					Gparent->color = red;
					break;
				}
			}
		}
		_head->_parent->color = black;
		_head->_right = rightMost();
		_head->_left = leftMost();
		return true;
	}

4.如何验证?

  • 先验证是否满足搜索二叉树。
  • 再验证是否满足红黑树的性质。
//判断是否是红黑树的条件:
	//不能连续两个红色
	//一个路径上黑色必须相同
	//root 必须为黑色
	bool IsRBTree()
	{
		Node* root = _head->_parent;
		if (root == nullptr)
			return true;
		if (root->color == red)
			return false;
		Node* cur = root;
		int BlackCout = 0;
		while (cur)
		{
			if (cur->color == black)
				BlackCout++;
			cur = cur->_left;           //找到一个基准值 然后遍历(前序)所有路径 如果都一样 则返回true
		}
		_IsRBTree(root, BlackCout, 0);
	}
	bool _IsRBTree(Node* root, int BlackCout, int NowCout)
	{
		if (root == nullptr)
		{
			if (BlackCout == NowCout)
				return true;
			return false;
		}
		if (root->color == black)
			NowCout++;
		if (root->color == red && root->_parent->color == red)
			return false;
		return _IsRBTree(root->_left, BlackCout, NowCout) && _IsRBTree(root->_right, BlackCout, NowCout);
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值