红黑树

1.红黑树的概念

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

2.性质

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

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

最短路径节点全黑,最长路径节点红黑交替(红不能连续出现)并且每条路径黑色节点数量相同,因此最长路径中节点数最多是最短路径节点数的两倍。

3.节点定义

// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class T>
struct RBTreeNode
{
	RBTreeNode(const T& data = T(),Color color = RED)
		: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
		, _data(data), _color(color)
	{}
	RBTreeNode<T>* _pLeft; 
	RBTreeNode<T>* _pRight;
	RBTreeNode<T>* _pParent;

	T _data;
	Color _color;
};

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

在节点插入时,若每次插入黑色节点,则每次都违反性质4,而插入红色节点,红黑树的性质可能不会改变,所以将节点设置为红色在插入节点时对红黑树造成的影响最小。

4.插入

红黑树节点插入时,性质三和性质四,我们实现插入时,就是为了维护性质3、4,在不同的情况下可以通过变色、变色+旋转去维护。

4.1 情况1(变色)

情况1:cur为红,p为红,g为黑,u存在且为红

当cur为新增节点时出现情况1:
在这里插入图片描述
当向上层变色时出现情况1:
在这里插入图片描述
变化到第3步后再判断g是否为根节点然后向上层处理。

情况1单纯通过变色就能维护红黑树的性质。

4.2 情况2(变色+单旋)

情况2:cur为红,p为红,g为黑,u不存在/u存在且为黑
当p为g的左孩子,cur为p的左孩子时,构成LL型,进行右单旋
当p为g的右孩子,cur为p的右孩子时,构成RR型,进行左单旋

下面演示其中一种
u不存在时:
在这里插入图片描述
u存在且为黑(这种情况不会在插入的地方出现,是在向上层变色中出现的):

在这里插入图片描述

4.3 情况3(变色+双旋)

情况3:cur为红,p为红,g为黑,u不存在/u存在且为黑
p为g的左孩子,cur为p的右孩子,构成LR型,右左双旋。
p为g的右孩子,cur为p的左孩子,构成RL型,左右双旋。

u不存在:
在这里插入图片描述
u存在且为黑:
在这里插入图片描述
一次单旋加换位后与情况2相同

为了后续封装map和set能更简单,这里实现红黑树时加入了一个头节点。
在这里插入图片描述
插入代码如下:

bool Insert(const T& data)
{
	Node*& pRoot = GetRoot();
	//将根节点设置为黑色
	if (nullptr == pRoot)
	{
		pRoot = new Node(data, BLACK);
		pRoot->_pParent = _pHead;
		return true;
	}
	else
	{
		Node* pCur = pRoot;
		Node* pParent = nullptr;
		//找到插入节点的位置
		while (pCur)
		{
			pParent = pCur;
			if (data < pCur->_data)
				pCur = pCur->_pLeft;
			else if (data > pCur->_data)
				pCur = pCur->_pRight;
			else
				return false;
		}
		//插入节点
		pCur = new Node(data, RED);
		if (data < pParent->_data)
			pParent->_pLeft = pCur;
		else
			pParent->_pRight = pCur;

		pCur->_pParent = pParent;
	//讨论刚才的几种情况
		while (pParent != _pHead && pParent->_color == RED)
		{
			Node* g = pParent->_pParent;
			if (pParent == g->_pLeft)
			{
				Node* u = g->_pRight;
				//u存在且为红
				if (u && RED == u->_color)
				{
					//情况1
					pParent->_color = BLACK;
					u->_color = BLACK;
					g->_color = RED;
					pCur = g;
					pParent = pCur->_pParent;
				}
				else
				{
					//u不存在或u为黑
					if (pCur == pParent->_pRight)
					{
						//情况3
						RotateL(pParent);
						swap(pCur, pParent);
					}
					//情况2
					pParent->_color = BLACK;
					g->_color = RED;
					RotateR(g);
				}
			}
			else
			{
				//一二三的反情况
				Node* u = g->_pLeft;
				if (u && RED == u->_color)
				{
					pParent->_color = BLACK;
					u->_color = BLACK;
					g->_color = RED;
					pCur = g;
					pParent = pCur->_pParent;
				}
				else
				{
					//u不存在或u为黑
					if (pCur == pParent->_pLeft)
					{
						//情况3的反情况
						RotateR(pParent);
						swap(pCur, pParent);
					}
					//情况2的反情况
					pParent->_color = BLACK;
					g->_color = RED;
					RotateL(g);
				}
			}
		}
	}
	pRoot->_color = BLACK;
	_pHead->_pLeft = LeftMost();
	_pHead->_pRight = RightMost();
	return true;
}

5.红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质
	bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
	{
		if (nullptr == pRoot)
			return true;
		if (pRoot->_color == BLACK)
			pathBlack++;
		Node* pParent = pRoot->_pParent;
		if (pParent != _pHead && RED == pParent->_color && RED == pRoot->_color)
		{
			cout << "违反性质3:不能存在连在一起的红色节点" << endl;
			return false;
		}
		if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
		{
			if (pathBlack != blackCount)
			{
				cout << "违反性质4:每条路径中黑色节点数相同" << endl;
				return false;
			}
		}
		return _IsValidRBTRee(pRoot->_pLeft, blackCount, pathBlack) &&
			_IsValidRBTRee(pRoot->_pRight, blackCount, pathBlack);
	}

bool IsValidRBTRee()
{
	Node* pRoot = GetRoot();
	if (nullptr == pRoot)
		return true;
	if (pRoot->_color != BLACK)
	{
		cout << "违反性质1:根节点是黑色" << endl;
		return false;
	}
	//获取一条路径中黑色节点数
	size_t blackCount = 0;
	Node* pCur = pRoot;
	while (pCur)
	{
		if (pCur->_color == BLACK)
			blackCount++;
		pCur = pCur->_pLeft;
	}
	size_t pathBlack = 0;
	return _IsValidRBTRee(pRoot, blackCount, pathBlack);
}
void InOrder()
{
	_InOrder(GetRoot());
	cout << endl;
}

6.源代码

#include<iostream>
using namespace std;




// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class T>
struct RBTreeNode
{
	RBTreeNode(const T& data = T(), Color color = RED)
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _color(color)
	{}


	RBTreeNode<T>* _pLeft;
	RBTreeNode<T>* _pRight;
	RBTreeNode<T>* _pParent;
	T _data;
	Color _color;
};




template<class T>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	RBTree()
	{
		_pHead = new Node;
		_pHead->_pLeft = _pHead;
		_pHead->_pRight = _pHead;
	}


	bool Insert(const T& data)
	{
		Node*& pRoot = GetRoot();
		//将根节点设置为黑色
		if (nullptr == pRoot)
		{
			pRoot = new Node(data, BLACK);
			pRoot->_pParent = _pHead;
			return true;
		}
		else
		{
			Node* pCur = pRoot;
			Node* pParent = nullptr;
			//找到插入节点的位置
			while (pCur)
			{
				pParent = pCur;
				if (data < pCur->_data)
					pCur = pCur->_pLeft;
				else if (data > pCur->_data)
					pCur = pCur->_pRight;
				else
					return false;
			}
			//插入节点
			pCur = new Node(data, RED);
			if (data < pParent->_data)
				pParent->_pLeft = pCur;
			else
				pParent->_pRight = pCur;


			pCur->_pParent = pParent;
		//讨论刚才的几种情况
			while (pParent != _pHead && pParent->_color == RED)
			{
				Node* g = pParent->_pParent;
				if (pParent == g->_pLeft)
				{
					Node* u = g->_pRight;
					//u存在且为红
					if (u && RED == u->_color)
					{
						//情况1
						pParent->_color = BLACK;
						u->_color = BLACK;
						g->_color = RED;
						pCur = g;
						pParent = pCur->_pParent;
					}
					else
					{
						//u不存在或u为黑
						if (pCur == pParent->_pRight)
						{
							//情况3
							RotateL(pParent);
							swap(pCur, pParent);
						}
						//情况2
						pParent->_color = BLACK;
						g->_color = RED;
						RotateR(g);
					}
				}
				else
				{
					//一二三的反情况
					Node* u = g->_pLeft;
					if (u && RED == u->_color)
					{
						pParent->_color = BLACK;
						u->_color = BLACK;
						g->_color = RED;
						pCur = g;
						pParent = pCur->_pParent;
					}
					else
					{
						//u不存在或u为黑
						if (pCur == pParent->_pLeft)
						{
							//情况3的反情况
							RotateR(pParent);
							swap(pCur, pParent);
						}
						//情况2的反情况
						pParent->_color = BLACK;
						g->_color = RED;
						RotateL(g);
					}
				}
			}
		}
		pRoot->_color = BLACK;
		_pHead->_pLeft = LeftMost();
		_pHead->_pRight = RightMost();
		return true;
	}


	// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
	Node* Find(const T& data)
	{
		Node* pRoot = GetRoot();
		if (nullptr == pRoot)return nullptr;
		while (pRoot)
		{
			if (pRoot->_data < data)
				pRoot = pRoot->_pRight;
			else if (pRoot&&pRoot->_data > data)
				pRoot = pRoot->_pLeft;
			else
				return pRoot;
		}
		return nullptr;
	}


	// 获取红黑树最左侧节点
	Node* LeftMost()
	{
		Node* pRoot = GetRoot();
		if (nullptr == pRoot)
			return _pHead;


		Node* pCur = pRoot;
		while (pCur->_pLeft)
			pCur = pCur->_pLeft;
		return pCur;
	}


	// 获取红黑树最右侧节点
	Node* RightMost()
	{
		Node* pRoot = GetRoot();
		if (nullptr == pRoot)
			return _pHead;


		Node* pCur = pRoot;
		while (pCur->_pRight)
			pCur = pCur->_pRight;
		return pCur;
	}


	// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
	bool IsValidRBTRee()
	{
		Node* pRoot = GetRoot();
		if (nullptr == pRoot)
			return true;
		if (pRoot->_color != BLACK)
		{
			cout << "违反性质1:根节点是黑色" << endl;
			return false;
		}
		//获取一条路径中黑色节点数
		size_t blackCount = 0;
		Node* pCur = pRoot;
		while (pCur)
		{
			if (pCur->_color == BLACK)
				blackCount++;
			pCur = pCur->_pLeft;
		}
		size_t pathBlack = 0;
		return _IsValidRBTRee(pRoot, blackCount, pathBlack);
	}
	void InOrder()
	{
		_InOrder(GetRoot());
		cout << endl;
	}
private:
	bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
	{
		if (nullptr == pRoot)
			return true;
		if (pRoot->_color == BLACK)
			pathBlack++;
		Node* pParent = pRoot->_pParent;
		if (pParent != _pHead && RED == pParent->_color && RED == pRoot->_color)
		{
			cout << "违反性质3:不能存在连在一起的红色节点" << endl;
			return false;
		}
		if (nullptr == pRoot->_pLeft && nullptr == pRoot->_pRight)
		{
			if (pathBlack != blackCount)
			{
				cout << "违反性质4:每条路径中黑色节点数相同" << endl;
				return false;
			}
		}
		return _IsValidRBTRee(pRoot->_pLeft, blackCount, pathBlack) &&
			_IsValidRBTRee(pRoot->_pRight, blackCount, pathBlack);
	}
	// 左单旋
	void RotateL(Node* pParent)
	{
		Node* pSubR = pParent->_pRight;
		Node* pSubRL = pSubR->_pLeft;


		pParent->_pRight = pSubRL;
		if (pSubRL)
			pSubRL->_pParent = pParent;


		pSubR->_pLeft = pParent;
		Node* pPParent = pParent->_pParent;
		pSubR->_pParent = pPParent;
		pParent->_pParent = pSubR;


		if (pPParent == _pHead)
			_pHead->_pParent = pSubR;
		else
		{
			if (pParent == pPParent->_pLeft)
				pPParent->_pLeft = pSubR;
			else
				pPParent->_pRight = pSubR;
		}
	}


	void RotateR(Node* pParent)
	{
		Node* pSubL = pParent->_pLeft;
		Node* pSubLR = pSubL->_pRight;


		pParent->_pLeft = pSubLR;
		if (pSubLR)
			pSubLR->_pParent = pParent;


		pSubL->_pRight = pParent;


		Node* pPParent = pParent->_pParent;
		pParent->_pParent = pSubL;
		pSubL->_pParent = pPParent;


		if (pPParent == _pHead)
			_pHead->_pParent = pSubL;
		else
		{
			if (pParent == pPParent->_pLeft)
				pPParent->_pLeft = pSubL;
			else
				pPParent->_pRight = pSubL;
		}
	}


	// 为了操作树简单起见:获取根节点
	Node*& GetRoot()
	{
		return _pHead->_pParent;
	}


	void _InOrder(Node* pRoot)
	{
		if (pRoot == nullptr)
		{
			return;
		}


		_InOrder(pRoot->_pLeft);
		cout << pRoot->_data << " ";
		_InOrder(pRoot->_pRight);
	}
private:


	Node* _pHead;
};

6.2 测试

int main()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14,8, 3, 1, 10, 6, 4, 7, 14, 13 };
	RBTree<int> t1;
	for (auto e : a)
	{
		if (e == 10)
		{
			int i = 0;
		}
		t1.Insert(e);


		cout << "Insert:" << e << "->" << t1.IsValidRBTRee() << endl;
	}


	t1.InOrder();


	cout << t1.IsValidRBTRee() << endl;
}

在这里插入图片描述

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gsfl

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

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

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

打赏作者

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

抵扣说明:

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

余额充值