红黑树性质+代码实现

一.红黑树概念

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

最长路径 <2 * 最短路径

二.红黑树性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 可以有连续的黑色节点,但不能有连续的红色节点。
4. 从根到null的每个路径上的黑色节点个数相同。
5. 每个空节点当成黑色节点。

三.函数实现

1.节点实现

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	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)
	{}
};

用enum枚举来定义颜色。

2.插入

首先对插入的节点定义颜色,是黑色还是红色?

如果定义为黑色,那么为了保持每条路径的黑色节点个数一致,我们还需对所有路径进行调整。而定义成红色,如果连的是黑色节点这不需要调整。就算是红色节点连续了,进行调整的范围不一定是对整颗子树。

所以插入节点定义为红色更好调整。

和AVL树插入一致,第一步都是去找对应的位置,小于在左边,大于在右边。

注意:插入节点后,要定义颜色。

	bool Insert(const pair<K, V> kv)
	{
		//没有节点直接插入
		if (_root == nullptr)
		{
			_root = new Node(kv);
            //根节点一定为黑色
			_root->_col = BLACK;
			return true;
		}
		//找对应的位置 有相同的值返回false
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else return false;
		}
		//判断该位置是左孩子还是右孩子
		cur = new Node(kv);
		cur->_col = RED;
		if (kv.first > parent->_kv.first) parent->_right = cur;
		else parent->_left = cur;

		cur->_parent = parent;

(cur插入节点 p插入节点的父母节点 g是p的父母节点 u是g的另一个孩子)

需要调整的情况分为

一.u存在且为红

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

二.u不存在/存在且为黑 这又要根据cur在p的位置分为单旋 双旋

1.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是直线,单旋)

2.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是曲线,双旋)

再根据u节点位置,又分为u在右边 u在左边两种情况

	while (parent&&parent->_col==RED)
	{
		Node* grandparent = parent->_parent;
		//u位置右边
		if (grandparent->_left == parent)
		{
			Node* uncle = grandparent->_right;
			//一.u存在且为红 变色+上调
			if (uncle && uncle->_col == RED)
			{
				grandparent->_col = RED;
				parent->_col = uncle->_col = BLACK;

				cur = grandparent;
				parent = cur->_parent;
			}
			//二.u不存在/存在为黑色
			else
			{
				//1.cur 位于p的左边 对g右单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
				if (cur == parent->_left)
				{
					RotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
				else
				{
					RotateL(parent);
					RotateR(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
				//处理完后 根节点为黑 各路径黑节点数相等
				break;
			}
		}
		//u位于左边
		else
		{
			Node* uncle = grandparent->_left;
			//1.u存在且为红 变色+上调
			if (uncle && uncle->_col == RED)
			{
				grandparent->_col = RED;
				parent->_col = uncle->_col = BLACK;

				cur = grandparent;
				parent = cur->_parent;
			}
			//2.u不存在/存在为黑色
			else
			{
				//1.cur 位于p的右边 对g左单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
				if (cur == parent->_right)
				{
					RotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
				else
				{
					RotateR(parent);
					RotateL(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
				//处理完后 根节点为黑 各路径黑节点数相等
				break;
			}
		}
	}
	//第一种情况u存在为红 g为根,p为空 循环结束,此时g根节点为红色 需要变色
	_root->_col = BLACK;
	return true;
}

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

解决方法:变色+上调

1.p u变黑 g变红 每条路径的黑色节点不变

2.上调。1.g就是根,没有父母节点。因为根必须为黑色,g变黑。

              2.g有父母节点,让cur=g p=cur->_parent 继续上调。

2.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是直线,单旋)

左图u存在且为黑色所以是经过情况一(u为红)cur变色后导致的情况。

此时单靠变色无法解决,对g进行右旋使黑色节点相等。

解决方法:单旋+变色

eg.

3.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是曲线,双旋)

解决方法:双旋+变色

总结:就是看u节点是否存在且为红色,是就只需要变色+上调。

反之就需要旋转+变色。具体单旋还是双旋就要看g p cur是否连成一条直线,能就单旋。

完整代码:
 

enum Colour
{
	RED,
	BLACK
};

template<class K, class V>
struct RBTreeNode
{
	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:
	RBTree() = default;

	RBTree(const RBTree<K, V>& t)
	{
		_root = Copy(t._root);
	}

	RBTree<K, V>& operator=(RBTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~RBTree()
	{
		Destroy(_root);
		_root = nullptr;
	}

	bool Insert(const pair<K, V> kv)
	{
		//没有节点直接插入
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}
		//找对应的位置 有相同的值返回false
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else return false;
		}
		//判断该位置是左孩子还是右孩子
		cur = new Node(kv);
		cur->_col = RED;
		if (kv.first > parent->_kv.first) parent->_right = cur;
		else parent->_left = cur;

		cur->_parent = parent;
		//调整
		while (parent&&parent->_col==RED)
		{
			Node* grandparent = parent->_parent;
			//u位置右边
			if (grandparent->_left == parent)
			{
				Node* uncle = grandparent->_right;
				//1.u存在且为红 变色+上调
				if (uncle && uncle->_col == RED)
				{
					grandparent->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandparent;
					parent = cur->_parent;
				}
				//2.u不存在/存在为黑色
				else
				{
					//1.cur 位于p的左边 对g右单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
					if (cur == parent->_left)
					{
						RotateR(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
					else
					{
						RotateL(parent);
						RotateR(grandparent);
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					//处理完后 根节点为黑 各路径黑节点数相等
					break;
				}
			}
			//u位于左边
			else
			{
				Node* uncle = grandparent->_left;
				//1.u存在且为红 变色+上调
				if (uncle && uncle->_col == RED)
				{
					grandparent->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandparent;
					parent = cur->_parent;
				}
				//2.u不存在/存在为黑色
				else
				{
					//1.cur 位于p的右边 对g左单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
					if (cur == parent->_right)
					{
						RotateR(grandparent);
						parent->_col = BLACK;
						grandparent->_col = RED;
					}
					//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
					else
					{
						RotateR(parent);
						RotateL(grandparent);
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					//处理完后 根节点为黑 各路径黑节点数相等
					break;
				}
			}
		}
		//第一种情况u存在为红 g为根,p为空 循环结束,此时g根节点为红色 需要变色
		_root->_col = BLACK;
		return true;
	}
	void Inorder()
	{
		_Inorder(_root);
		return;
	}
private:
	Node* _root = nullptr;
	void _Inorder(Node* root)
	{
		if (root == nullptr) return;
		_Inorder(root->_left);
		cout << root->_kv.first << "->" << root->_kv.second << ' ';
		_Inorder(root->_right);
	}
	Node* Copy(Node* root, Node* parent)
	{

		if (root == nullptr) return nullptr;
		Node* cur = new Node(root->_kv);
		cur->_parent = parent;
		cur->_left = Copy(root->_left, cur);
		cur->_right = Copy(root->_right, cur);
		return cur;
	}
	void Destroy(Node* root)
	{
		if (root == nullptr) return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentP = parent->_parent;
		parent->_right = subRL;
		if (subRL) subRL->_parent = parent;
		subR->_left = parent;
		parent->_parent = subR;
		if (parentP == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			subR->_parent = parentP;
			if (parentP->_left == parent) parentP->_left = subR;
			else parentP->_right = subR;
		}
		
	}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentP = parent->_parent;

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

	}
};

  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值