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;
}

 

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