C (map/multimap/set/multiset)底层实现---红黑树

红黑树(R-B TREE,全称:Red-Black Tree),本身是一棵二叉查找树,在其基础上附加了两个要求:

  1. 树中的每个结点增加了一个用于存储颜色的标志域;
  2. 树中没有一条路径比其他任何路径长出两倍,整棵树要接近于“平衡”的状态

        这里所指的路径,指的是从任何一个结点开始,一直到其子孙的叶子结点的长度;接近于平衡:红黑树并不是平衡二叉树,只是由于对各路径的长度之差有限制,所以近似于平衡的状态。

红黑树的性质

1. 每个结点不是红色就是黑色

2. 根节点是黑色的 

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树节点的定义

enum Color
{
	BLACK,
	RED
};

template <class K, class V>
struct RBTNode
{
	typedef RBTNode<K, V> Node;
	Node* _pLeft = nullptr;
	Node* _pRight = nullptr;
	Node* _pParent = nullptr;
	pair<K, V> _kv;
	Color _color = RED;
};

红黑树本身作为一棵二叉查找树,所以其任务就是用于动态表中数据的插入和删除的操作。在进行该操作时,避免不了会破坏红黑树的结构,此时就需要进行适当的调整,使其重新成为一棵红黑树,可以从两个方面着手对树进行调整:

  • 调整树中某些结点的指针结构;
  • 调整树中某些结点的颜色;

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

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

typedef RBTNode<K, V> Node;
	typedef Node* pNode;
	typedef Node* PNode;
	RBTree()
	{
		_header = new Node;
		_header->_pParent = nullptr;
		_header->_pLeft = _header;
		_header->_pRight = _header;
	}

	bool Insert(const pair<K, V>& kv)
	{
		if (_header->_pParent == nullptr)
		{
			pNode root = new Node;
			root->_kv = kv;
			root->_color = BLACK;

			root->_pParent = _header;
			_header->_pParent = root;

			_header->_pLeft = root;
			_header->_pRight = root;

			return true;
		}

		pNode cur = _header->_pParent;
		pNode parent = nullptr;

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

		pNode newNode = new Node;
		newNode->_kv = kv;
		if (kv.first > parent->_kv.first)
			parent->_pRight = newNode;
		else
			parent->_pLeft = newNode;

		newNode->_pParent = parent;

		cur = newNode;

		while (cur != _header->_pParent && cur->_pParent->_color == RED)
		{
			// cur, parent, gParent, uncle
			pNode parent = cur->_pParent;
			pNode gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
			{
				pNode uncle = gParent->_pRight;
				// u存在且为红
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
				}
				else
				{
					//u不存在/  u存在且为黑
					//旋转,调色
					//判断是否需要双旋
					if (cur == parent->_pRight)
					{
						RotateL(parent);
						swap(parent, cur);
					}

					//右单旋
					RotateR(gParent);
					parent->_color = BLACK;
					gParent->_color = RED;
					break;
				}
			}
			else
			{
				pNode uncle = gParent->_pLeft;
				//对应
				if (uncle && uncle->_color == RED)
				{
					uncle->_color = parent->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
				}
				else
				{
					//判断是否要双旋
					if (cur == parent->_pLeft)
					{
						RotateR(parent);
						swap(cur, parent);
					}

					RotateL(gParent);
					parent->_color = BLACK;
					gParent->_color = RED;
					break;
				}
			}

		}

		_header->_pParent->_color = BLACK;
		_header->_pLeft = leftMost();
		_header->_pRight = rightMost();
		return true;
	}

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

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

       约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

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

情况二: cur为红,p为红,g为黑,u不存在/u为黑

情况二: cur为红,p为红,g为黑,u不存在/u为黑

 

// 获取红黑树中最小节点,即最左侧节点
	pNode leftMost()
	{
		pNode cur = _header->_pParent;
		while (cur && cur->_pLeft)
			cur = cur->_pLeft;
		return cur;
	}
	// 获取红黑树中最大节点,即最右侧节点
	pNode rightMost()
	{
		pNode cur = _header->_pParent;
		while (cur && cur->_pRight)
			cur = cur->_pRight;
		return cur;
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;

		subR->_pLeft = parent;
		parent->_pRight = subRL;

		if (subRL)
			subRL->_pParent = parent;

		if (parent != _header->_pParent)
		{
			Node* gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
				gParent->_pLeft = subR;
			else
				gParent->_pRight = subR;
			subR->_pParent = gParent;
		}
		else
		{
			subR->_pParent = nullptr;
			_header->_pParent = subR;
		}

		parent->_pParent = subR;

	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_pLeft;
		Node* subLR = subL->_pRight;

		//单向链接 subL , parent, subLR
		subL->_pRight = parent;
		parent->_pLeft = subLR;

		//向上链接subLR
		if (subLR)
			subLR->_pParent = parent;

		//subL,parent->parent 双向链接
		if (parent != _header->_pParent)
		{
			Node* gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
				gParent->_pLeft = subL;
			else
				gParent->_pRight = subL;
			subL->_pParent = gParent;
		}
		else
		{
			_header->_pParent = subL;
			subL->_pParent = nullptr;
		}

		//向上链接parent, subL
		parent->_pParent = subL;

	}

 

 

红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2. 检测其是否满足红黑树的性质

void _Inorder(pNode root)
	{
		if (root)
		{
			_Inorder(root->_pLeft);
			cout << root->_kv.first << "-->" << root->_kv.second << endl;
			_Inorder(root->_pRight);
		}
	}

	void Inorder()
	{
		_Inorder(_header->_pParent);
	}

	bool IsValidRBTree()
	{
		PNode pRoot = _header->_pParent;
		// 空树也是红黑树
		if (nullptr == pRoot)
			return true;
	
			// 检测根节点是否满足情况
		if (BLACK != pRoot->_color)
		{
			cout << "违反红黑树性质二:根节点必须为黑色" << endl;
			return false;
		}
		// 获取任意一条路径中黑色节点的个数
		size_t blackCount = 0;
		PNode pCur = pRoot;
		while (pCur)
		{
			if (BLACK == pCur->_color)
				blackCount++;
			pCur = pCur->_pLeft;
		}
		// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
		size_t k = 0;
		return _IsValidRBTree(pRoot, k, blackCount);
	}
	bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
	{
		//走到null之后,判断k和black是否相等
		if (nullptr == pRoot)
		{
			if (k != blackCount)
			{
				cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
				return false;
			}
			return true;
		}
		// 统计黑色节点的个数
		if (BLACK == pRoot->_color)
			k++;
		// 检测当前节点与其双亲是否都为红色
		PNode pParent = pRoot->_pParent;
		if (pParent && RED == pParent->_color && RED == pRoot->_color)
		{
			cout << "违反性质三:没有连在一起的红色节点" << endl;
			return false;
		}
		return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
			_IsValidRBTree(pRoot->_pRight, k, blackCount);
	}

代码整合

#include <iostream>
#include <time.h>
using namespace std;

enum Color
{
	BLACK,
	RED
};

template <class K, class V>
struct RBTNode
{
	typedef RBTNode<K, V> Node;
	Node* _pLeft = nullptr;
	Node* _pRight = nullptr;
	Node* _pParent = nullptr;
	pair<K, V> _kv;
	Color _color = RED;
};

template <class K, class V>
class RBTree
{
public:
	typedef RBTNode<K, V> Node;
	typedef Node* pNode;
	typedef Node* PNode;
	RBTree()
	{
		_header = new Node;
		_header->_pParent = nullptr;
		_header->_pLeft = _header;
		_header->_pRight = _header;
	}

	bool Insert(const pair<K, V>& kv)
	{
		if (_header->_pParent == nullptr)
		{
			pNode root = new Node;
			root->_kv = kv;
			root->_color = BLACK;

			root->_pParent = _header;
			_header->_pParent = root;

			_header->_pLeft = root;
			_header->_pRight = root;

			return true;
		}

		pNode cur = _header->_pParent;
		pNode parent = nullptr;

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

		pNode newNode = new Node;
		newNode->_kv = kv;
		if (kv.first > parent->_kv.first)
			parent->_pRight = newNode;
		else
			parent->_pLeft = newNode;

		newNode->_pParent = parent;

		cur = newNode;

		while (cur != _header->_pParent && cur->_pParent->_color == RED)
		{
			// cur, parent, gParent, uncle
			pNode parent = cur->_pParent;
			pNode gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
			{
				pNode uncle = gParent->_pRight;
				// u存在且为红
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
				}
				else
				{
					//u不存在/  u存在且为黑
					//旋转,调色
					//判断是否需要双旋
					if (cur == parent->_pRight)
					{
						RotateL(parent);
						swap(parent, cur);
					}

					//右单旋
					RotateR(gParent);
					parent->_color = BLACK;
					gParent->_color = RED;
					break;
				}
			}
			else
			{
				pNode uncle = gParent->_pLeft;
				//对应
				if (uncle && uncle->_color == RED)
				{
					uncle->_color = parent->_color = BLACK;
					gParent->_color = RED;
					cur = gParent;
				}
				else
				{
					//判断是否要双旋
					if (cur == parent->_pLeft)
					{
						RotateR(parent);
						swap(cur, parent);
					}

					RotateL(gParent);
					parent->_color = BLACK;
					gParent->_color = RED;
					break;
				}
			}

		}

		_header->_pParent->_color = BLACK;
		_header->_pLeft = leftMost();
		_header->_pRight = rightMost();
		return true;
	}
	// 获取红黑树中最小节点,即最左侧节点
	pNode leftMost()
	{
		pNode cur = _header->_pParent;
		while (cur && cur->_pLeft)
			cur = cur->_pLeft;
		return cur;
	}
	// 获取红黑树中最大节点,即最右侧节点
	pNode rightMost()
	{
		pNode cur = _header->_pParent;
		while (cur && cur->_pRight)
			cur = cur->_pRight;
		return cur;
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;

		subR->_pLeft = parent;
		parent->_pRight = subRL;

		if (subRL)
			subRL->_pParent = parent;

		if (parent != _header->_pParent)
		{
			Node* gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
				gParent->_pLeft = subR;
			else
				gParent->_pRight = subR;
			subR->_pParent = gParent;
		}
		else
		{
			subR->_pParent = nullptr;
			_header->_pParent = subR;
		}

		parent->_pParent = subR;

	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_pLeft;
		Node* subLR = subL->_pRight;

		//单向链接 subL , parent, subLR
		subL->_pRight = parent;
		parent->_pLeft = subLR;

		//向上链接subLR
		if (subLR)
			subLR->_pParent = parent;

		//subL,parent->parent 双向链接
		if (parent != _header->_pParent)
		{
			Node* gParent = parent->_pParent;
			if (gParent->_pLeft == parent)
				gParent->_pLeft = subL;
			else
				gParent->_pRight = subL;
			subL->_pParent = gParent;
		}
		else
		{
			_header->_pParent = subL;
			subL->_pParent = nullptr;
		}

		//向上链接parent, subL
		parent->_pParent = subL;

	}

	void _Inorder(pNode root)
	{
		if (root)
		{
			_Inorder(root->_pLeft);
			cout << root->_kv.first << "-->" << root->_kv.second << endl;
			_Inorder(root->_pRight);
		}
	}

	void Inorder()
	{
		_Inorder(_header->_pParent);
	}

	bool IsValidRBTree()
	{
		PNode pRoot = _header->_pParent;
		// 空树也是红黑树
		if (nullptr == pRoot)
			return true;
	
			// 检测根节点是否满足情况
		if (BLACK != pRoot->_color)
		{
			cout << "违反红黑树性质二:根节点必须为黑色" << endl;
			return false;
		}
		// 获取任意一条路径中黑色节点的个数
		size_t blackCount = 0;
		PNode pCur = pRoot;
		while (pCur)
		{
			if (BLACK == pCur->_color)
				blackCount++;
			pCur = pCur->_pLeft;
		}
		// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
		size_t k = 0;
		return _IsValidRBTree(pRoot, k, blackCount);
	}
	bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount)
	{
		//走到null之后,判断k和black是否相等
		if (nullptr == pRoot)
		{
			if (k != blackCount)
			{
				cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
				return false;
			}
			return true;
		}
		// 统计黑色节点的个数
		if (BLACK == pRoot->_color)
			k++;
		// 检测当前节点与其双亲是否都为红色
		PNode pParent = pRoot->_pParent;
		if (pParent && RED == pParent->_color && RED == pRoot->_color)
		{
			cout << "违反性质三:没有连在一起的红色节点" << endl;
			return false;
		}
		return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&
			_IsValidRBTree(pRoot->_pRight, k, blackCount);
	}


private:
	pNode _header;
};

void testRBTree()
{
	RBTree<int, int> rbtree;
	rbtree.Insert(make_pair(0, 0));
	rbtree.Insert(make_pair(1, 0));
	rbtree.Insert(make_pair(2, 0));
	rbtree.Insert(make_pair(10, 0));
	rbtree.Insert(make_pair(3, 0));
	rbtree.Insert(make_pair(9, 0));
	


	rbtree.Inorder();
	cout << "IsValidRBTree: " << rbtree.IsValidRBTree() << endl;
}

// void testRBTree2()
// {
	// srand(time(nullptr));
	// int n;
	// cin >> n;
	// RBTree<int, int> rb;
	// while (n--)
	// {
		// int num = rand();
		// //cout << num << " ";
		// rb.Insert(make_pair(num, num));
	// }
	// cout << endl;
	// cout << "IsValidRBTree: " << rb.IsValidRBTree() << endl;
// }

int main()
{
	testRBTree();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章