關聯式容器構造【紅黑樹】分析與運用

紅黑樹概念

紅黑樹,是一種二叉搜索樹,但在每個結點上增加一個存儲位表示結點的顏色,可以是Red或Black。 通過對任何一條從根到葉子的路徑上各個結點着色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,是接近平衡的

紅黑樹的性質

  1. 每個結點不是紅色就是黑色
    比特科技
  2. 根節點是黑色的
  3. 如果一個節點是紅色的,則它的兩個孩子結點是黑色的
  4. 對於每個結點,從該結點到其所有後代葉結點的簡單路徑上,均 包含相同數目的黑色結點
  5. 每個葉子結點都是黑色的(此處的葉子結點指的是空結點)

紅黑樹的實現

結構

爲了後續實現關聯式容器簡單,紅黑樹的實現中增加一個頭結點,因爲跟節點必須爲黑色,爲了與根節點進行區分,將頭結點給成黑色,並且讓頭結點的 pParent 域指向紅黑樹的根節點,pLeft域指向紅黑樹中最小的節點,_pRight域指向紅黑樹中最大的節點
在這裏插入圖片描述

定義

enum COLOR//枚舉
{
	BLACK,
	RED
};
template <class K, class V>
struct RBNode
{
	RBNode<K, V>* _left;
	RBNode<K, V>* _right;
	RBNode<K, V>* _parent;
	pair<K, V> _value;
	//顏色
	COLOR _color;

	RBNode(const pair<K, V>& value = pair<K, V>())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _value(value)
		, _color(RED)
	{}
};

節點的定義中,要將節點的默認顏色給成紅色的

插入

分爲兩個過程:

  1. 按照二叉搜索的樹規則插入新節點
  2. 調整更新,檢測新節點插入後,紅黑樹的性質是否造到破壞

情況一: cur爲紅,p爲紅,g爲黑,u存在且爲紅
在這裏插入圖片描述
解決方式:將p,u改爲黑,g改爲紅,然後把g當成cur,繼續向上調整

情況二: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
在這裏插入圖片描述
解決方式:p爲g的左孩子,cur爲p的左孩子,則進行右單旋轉;相反,p爲g的右孩子,cur爲p的右孩子,則進行左單旋轉,p、g變色–p變黑,g變紅

情況三: cur爲紅,p爲紅,g爲黑,u不存在/u爲黑
在這裏插入圖片描述
解決方式:p爲g的左孩子,cur爲p的右孩子,則針對p做左單旋轉;相反,p爲g的右孩子,cur爲p的左孩子,則針對p做右單旋轉,則轉換成了情況2

相較於 AVL 樹,插入比較簡單,且效率比較高,紅黑樹比AVL 樹調整次數少

上面三種情況即可總結爲:

在這裏插入圖片描述

檢驗紅黑樹

  1. 根節點爲黑色
  2. 紅色不連續
  3. 每條路徑黑色節點數相同

代碼

#include <utility>
#include <iostream>
using namespace std;

enum COLOR
{
	BLACK,
	RED
};
//節點存放<K,V>數據
template <class K, class V>
struct RBNode
{
	RBNode<K, V>* _left;
	RBNode<K, V>* _right;
	RBNode<K, V>* _parent;
	pair<K, V> _value;
	//顏色
	COLOR _color;

	RBNode(const pair<K, V>& value = pair<K, V>())
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _value(value)
		, _color(RED)
	{}
};

template <class K, class V>
class RBTree
{
public:
	typedef RBNode<K, V> Node;
	typedef Node* pNode;

	RBTree()
	{
		//構建空的紅黑樹
		_header = new Node();
		_header->_left = _header;
		_header->_right = _header;
	}

	bool insert(const pair<K, V>& value)
	{
		//搜索樹的插入
		if (_header->_parent == nullptr)
		{
			//空樹,創建根節點
			pNode root = new Node(value);
			root->_color = BLACK;
			root->_parent = _header;
			_header->_parent = root;

			_header->_left = root;
			_header->_right = root;
			return true;
		}
		//從根開始搜索
		pNode cur = _header->_parent;
		pNode parent = nullptr;
		while (cur)
		{
			parent = cur;
			//按照key值確定位置, key不能重複
			if (cur->_value.first == value.first)
				return false;
			else if (cur->_value.first > value.first)
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		cur = new Node(value);
		if (parent->_value.first > cur->_value.first)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//調整和更新(顏色): 連續的紅色時需要調整
		while (cur != _header->_parent && cur->_parent->_color == RED)
		{
			// cur, parent, gfather, uncle
			parent = cur->_parent;
			pNode gfather = parent->_parent;
			if (gfather->_left == parent)
			{
				pNode uncle = gfather->_right;
				//uncle 存在且爲紅
				if (uncle && uncle->_color == RED)
				{
					//修改顏色
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;
					//繼續向上更新
					cur = gfather;
				}
				else{
					//如果存在雙旋的場景,可以先進行一次單旋,使它變成單旋的場景
					if (cur == parent->_right)
					{
						RotateL(parent);
						swap(cur, parent);
					}
					//右旋
					RotateR(gfather);
					//修改顏色
					parent->_color = BLACK;
					gfather->_color = RED;
					//停止調整
					break;
				}
			}
			else
			{
				pNode uncle = gfather->_left;
				if (uncle && uncle->_color == RED)
				{
					//修改顏色
					uncle->_color = parent->_color = BLACK;
					gfather->_color = RED;
					cur = gfather;
				}
				else{
					//判斷是否有雙旋的場景
					if (cur == parent->_left)
					{
						RotateR(parent);
						swap(cur, parent);
					}
					RotateL(gfather);
					parent->_color = BLACK;
					gfather->_color = RED;
					break;
				}
			}

		}
		//根的顏色始終是黑的
		_header->_parent->_color = BLACK;
		//更新 _header->_left, _header->_right
		_header->_left = leftMost();
		_header->_right = rightMost();
		return true;
	}

	pNode leftMost()
	{
		pNode cur = _header->_parent;
		while (cur && cur->_left != nullptr)
		{
			cur = cur->_left;
		}
		return cur;
	}

	pNode rightMost()
	{
		pNode cur = _header->_parent;
		while (cur && cur->_right != nullptr)
		{
			cur = cur->_right;
		}
		return cur;
	}

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

		// 1
		subL->_right = parent;
		// 2
		parent->_left = subLR;
		// 3
		if (subLR)
			subLR->_parent = parent;
		// 4,  5
		if (parent != _header->_parent)
		{
			// subL <---> parent->parent
			pNode gParent = parent->_parent;
			if (gParent->_left == parent)
				gParent->_left = subL;
			else
				gParent->_right = subL;
			subL->_parent = gParent;
		}
		else
		{
			//更新根節點
			_header->_parent = subL;
			subL->_parent = nullptr;
		}
		// 6
		parent->_parent = subL;

	}

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

		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		if (parent != _header->_parent){
			pNode gParent = parent->_parent;
			if (gParent->_left == parent)
				gParent->_left = subR;
			else
				gParent->_right = subR;
			subR->_parent = gParent;
		}
		else
		{
			_header->_parent = subR;
			subR->_parent = nullptr;
		}
		parent->_parent = subR;
	}

	void inOrder()
	{
		_inOrder(_header->_parent);
		cout << endl;
	}

	void _inOrder(pNode root)
	{
		if (root)
		{
			_inOrder(root->_left);
			cout << "< " << root->_value.first << "--->" << root->_value.second << "> ";
			_inOrder(root->_right);
		}
	}

    bool isRBTree()
	{
		pNode root = _header->_parent;
		if (root == nullptr)
			return true;
		if (root->_color == RED)
		{
			cout << "根節點必須爲黑色" << endl;
			return false;
		}
		//每條路徑黑色個數相同
		//可以任意遍歷一條路徑
		//此處爲最右路徑
		pNode cur = root;
		int blackCount = 0;
		while (cur)
		{
			if (cur->_color == BLACK)
				++blackCount;
			cur = cur->_right;
		}
		int k = 0;
		return _isRBTree(root, k, blackCount);
	}

	bool _isRBTree(pNode root, int curBlackCount, int totalBlackCount)
	{
		//每條路徑上黑色個數相同
		//一條路徑走完
		if (root == nullptr)
		{
			cout << "每條路徑黑色節點個數不同" << endl;
			return false;
		}
		return true;

		if (root->_color == Black)
			++curBlackCount;

		//沒有紅色連續
		pNode parent = root->_parent;
		if (parent && parent->_color == RED && root->_color == RED)
		{
			cout << "有紅色連續的節點" << endl;
			return false;
		}
		return _isRBTree(root->_left, curBlackCount, totalBlackCount) && _isRBTree(root->_right, curBlackCount, totalBlackCount);
	}

private:
	pNode _header;
};

void testRBTree()
{
	RBTree<int, int> rbt;
	rbt.insert(make_pair(1, 1));
	rbt.insert(make_pair(10, 1));
	rbt.insert(make_pair(-1, 1));
	rbt.insert(make_pair(-2, 1));
	rbt.insert(make_pair(100, 1));
	rbt.insert(make_pair(19, 1));
	rbt.insert(make_pair(21, 1));
	rbt.inOrder();
}

int main()
{
	testRBTree();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章