数据结构之红黑树(RBTree)

数据结构之红黑树(RBTree)



一、红黑树的概念

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

二、红黑树的性质

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

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

三、红黑树节点的定义

enum Color
{
	Red,
	Black
};

template <class K,class V>
struct TreeNode
{
	TreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(Red)
	{}

	TreeNode* _left;
	TreeNode* _right;
	TreeNode* _parent;
	pair<K, V> _kv;
	Color _col;
};

思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?

四、红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
在这里插入图片描述

五、红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点

先按照搜索二叉树的规则找到插入节点的位置

bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first > kv.first)
		{
			cur = new Node(kv);
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			cur = new Node(kv);
			parent->_right = cur;
			cur->_parent = parent;
		}


*******************************************************************
	}

2. 检测新节点插入后,红黑树的性质是否造到破坏

新增:插入红色节点
1、插入节点的父亲是黑色,那么就结束了。没有违反任何规则。
2、插入节点的父亲是红色的,那么存在连续的红色节点,违反规则3,需要处理
关键看叔叔uncle其他几个节点c p g三个颜色固定
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

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

这里大分类有两类(逻辑分类):

  1. uncle存在且为红色(不需要判断位置关系)
  2. uncle存在且为黑色或者uncle不存在(需要判断位置关系,继续分类)

但实际转化为代码时,需要看cur p g u三者之间的位置关系来分类,因为不同位置调整的方式不同

  1. p在g的左边,u在g的右边 或者 p在g的右边,u在g的左边
  2. p如果在左边,cur也在左边 或者 p在右边,cur也在右边
  3. p如果在左边,cur在右边 或者 p在右边,cur在左边

在这三种位置关系的情况下,再结合逻辑分类进行分类

以下是分类逻辑图
在这里插入图片描述

情况一

这种情况下 uncle存在且为红色
在这里插入图片描述

情况二

分为两个小情况

  1. cur在这棵树的外侧
    在这里插入图片描述

  2. cur在这棵树的内侧

在这里插入图片描述

3. 插入代码

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first > kv.first)
		{
			cur = new Node(kv);
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			cur = new Node(kv);
			parent->_right = cur;
			cur->_parent = parent;
		}

		
		while (parent && parent->_col == Red)
		{
			if (parent->_col == Black)
			{
				break;
			}

			//parent是红色,需要先找祖父,再寻找uncle
			Node* grandparent = parent->_parent;
			Node* uncle = nullptr;



			//parent在左 uncle在右
			if (grandparent->_left == parent)
			{
				uncle = grandparent->_right;

				
				//uncle存在且为红色
				if (uncle && uncle->_col == Red)
				{
					parent->_col = Black;
					uncle->_col = Black;
					grandparent->_col = Red;

					//继续向上更新
					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle存在且为黑色或者uncle不存在
				else
				{
					//cur在左,右单旋
					if (cur == parent->_left)
					{
						RotateR(grandparent);

						parent->_col = Black;
						grandparent->_col = Red;

						break;
					}
					//cur在右,双旋
					else
					{
						RotateL(parent);
						RotateR(grandparent);

						cur->_col = Black;
						grandparent->_col = Red;

						break;
					}
				}
				

			}
			//parent在右 uncle在左
			else
			{
				uncle = grandparent->_left;

				//uncle存在且为红色
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandparent->_col = Red;

					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle存在且为黑色或uncle不存在
				else
				{
					//cur在右,左单旋
					if (cur == parent->_right)
					{
						RotateL(grandparent);

						parent->_col = Black;
						grandparent->_col = Red;

						break;
					}
					//cur在左,双旋
					else
					{
						RotateR(parent);
						RotateL(grandparent);

						cur->_col = Black;
						grandparent->_col = Red;

						break;
					}

				}

			}

			
		}

		_root->_col = Black;
		return true;
	}

六、红黑树的验证

根据红黑树的规则,我们可以中序遍历这棵树打印值来检验是否符合规则,但仅仅这样是不能完全检测出的,我们需要根据红黑树的规则,来设计函数来检测这棵树

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

规则1与2只需再开始时检测一边
规则3如果之间判断会不好判断,但我们可以转换思路,如果该节点是红色的,那么他的父节点一定是黑色的,如果是红色就是违背了规则
规则4需要在每条路径走到空的时候与参考值比较,不相等就违背规则,参考值是在调用之前算出某条路径的黑色节点数

	bool Isbalance()
	{
		if (_root == nullptr)
		{
			return true;
		}

		if (_root->_col == Red)
		{
			return false;
		}

		size_t reference = 0;
		
		Node* cur = _root;
		
		while (cur)
		{
			if (cur->_col == Black)
			{
				reference++;
			}
			cur = cur->_left;
		}

		return _Isbalance(_root, reference, 0);
	}


bool _Isbalance(Node* root, size_t reference, size_t bnum)
	{
		if (root == nullptr)
		{
			if (bnum != reference)
			{
				cout << "每条路径黑色节点个数不同" << endl;
				return false;
			}
			return true;
		}

		if (root->_col == Red)
		{
			if (root->_parent->_col != Black)
			{
				cout << "出现连续的红色节点" << endl;
				return false;
			}
		}
		else
		{
			bnum++;
		}

		return _Isbalance(root->_left, reference, bnum) && _Isbalance(root->_right, reference, bnum);
	}

七、整体代码(含测试)

RBTree.h

#include <iostream>
using namespace std;

enum Color
{
	Red,
	Black
};

template <class K,class V>
struct TreeNode
{
	TreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(Red)
	{}

	TreeNode* _left;
	TreeNode* _right;
	TreeNode* _parent;
	pair<K, V> _kv;
	Color _col;
};

template <class K, class V>
class RBTree
{
	typedef TreeNode<K, V> Node;

public:
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if(cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first > kv.first)
		{
			cur = new Node(kv);
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			cur = new Node(kv);
			parent->_right = cur;
			cur->_parent = parent;
		}

		
		while (parent && parent->_col == Red)
		{
			if (parent->_col == Black)
			{
				break;
			}

			//parent是红色,需要先找祖父,再寻找uncle
			Node* grandparent = parent->_parent;
			Node* uncle = nullptr;



			//parent在左 uncle在右
			if (grandparent->_left == parent)
			{
				uncle = grandparent->_right;

				
				//uncle存在且为红色
				if (uncle && uncle->_col == Red)
				{
					parent->_col = Black;
					uncle->_col = Black;
					grandparent->_col = Red;

					//继续向上更新
					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle存在且为黑色或者uncle不存在
				else
				{
					//cur在左,右单旋
					if (cur == parent->_left)
					{
						RotateR(grandparent);

						parent->_col = Black;
						grandparent->_col = Red;

						break;
					}
					//cur在右,双旋
					else
					{
						RotateL(parent);
						RotateR(grandparent);

						cur->_col = Black;
						grandparent->_col = Red;

						break;
					}
				}
				

			}
			//parent在右 uncle在左
			else
			{
				uncle = grandparent->_left;

				//uncle存在且为红色
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandparent->_col = Red;

					cur = grandparent;
					parent = cur->_parent;
				}
				//uncle存在且为黑色或uncle不存在
				else
				{
					//cur在右,左单旋
					if (cur == parent->_right)
					{
						RotateL(grandparent);

						parent->_col = Black;
						grandparent->_col = Red;

						break;
					}
					//cur在左,双旋
					else
					{
						RotateR(parent);
						RotateL(grandparent);

						cur->_col = Black;
						grandparent->_col = Red;

						break;
					}

				}

			}

			
		}

		_root->_col = Black;
		return true;
	}

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

		parent->_right = subRL;
		subR->_left = parent;

		Node* parentparent = parent->_parent;

		if (subRL)
			subRL->_parent = parent;

		parent->_parent = subR;

		if (parent == _root)
		{
			subR->_parent = nullptr;
			_root = subR;
		}
		else
		{
			subR->_parent = parentparent;

			if (parentparent->_left == parent)
			{
				parentparent->_left = subR;
			}
			else
			{
				parentparent->_right = subR;
			}
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		subL->_right = parent;

		Node* parentparent = parent->_parent;

		if (subLR)
		{
			subLR->_parent = parent;
		}
		parent->_parent = subL;

		if (parent == _root)
		{
			subL->_parent = nullptr;
			_root = subL;
		}
		else
		{
			subL->_parent = parentparent;

			if (parentparent->_left == parent)
			{
				parentparent->_left = subL;
			}
			else
			{
				parentparent->_right = subL;
			}
		}
	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool Isbalance()
	{
		if (_root == nullptr)
		{
			return true;
		}

		if (_root->_col == Red)
		{
			return false;
		}

		size_t reference = 0;
		
		Node* cur = _root;
		
		while (cur)
		{
			if (cur->_col == Black)
			{
				reference++;
			}
			cur = cur->_left;
		}

		return _Isbalance(_root, reference, 0);
	}



private:

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

	bool _Isbalance(Node* root, size_t reference, size_t bnum)
	{
		if (root == nullptr)
		{
			if (bnum != reference)
			{
				cout << "每条路径黑色节点个数不同" << endl;
				return false;
			}
			return true;
		}

		if (root->_col == Red)
		{
			if (root->_parent->_col != Black)
			{
				cout << "出现连续的红色节点" << endl;
				return false;
			}
		}
		else
		{
			bnum++;
		}

		return _Isbalance(root->_left, reference, bnum) && _Isbalance(root->_right, reference, bnum);
	}


	Node* _root = nullptr;
};

RBTree.c

#include "RBTree.h"
#define N 1000

int main()
{
	srand((unsigned int)time(NULL));

	RBTree<int, int> t;
	for (int i = 0; i < N; i++)
	{
		int sum = rand();

		cout << "插入数据为" << sum;

		t.Insert(make_pair(sum, sum));
		if (t.Isbalance())
		{
			cout << "插入后符合规则" << endl;
		}
		else
		{
			cout << "插入后不符合规则" << endl;
		}
	}

	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值