平衡搜索樹--紅黑樹 RBTree

紅黑樹是一棵二叉搜索樹,它在每一個節點上增長了一個存儲位來表示節點的顏色,能夠是RedBlackgit

經過對任何一條從根到葉子節點簡單路徑上的顏色來約束樹的高度,紅黑樹保證最長路徑不超過最短路徑的兩倍,於是近似於平衡。github

紅黑樹是知足下面紅黑性質的二叉搜索樹:算法

1. 每一個節點,不是紅色就是黑色的 函數

2. 根節點是黑色的 性能

3. 若是一個節點是紅色的,則它的兩個子節點是黑色的(不存在連續的紅色節點)spa

4. 對每一個節點,從該節點到其全部後代葉節點的簡單路徑上,均包含相同數目的黑色節點。3d

 

思考:爲何知足上面的顏色約束性質,紅黑樹能保證最長路徑不超過最短路徑的兩倍?指針

  最短的路徑上節點的顏色所有都爲黑色;最長的路徑則爲黑紅交叉的路徑,其上有與最短路徑的黑節點數目相同的黑節點數和紅節點數目。因此咱們按照紅黑樹性質所創建的紅黑樹的最長路徑必然不會超過最短路徑的兩倍!blog

創建紅黑樹的節點類:遞歸

插入的新節點默認是紅色的。緣由是:插入黑節點必然會影響全部路徑都含有相同數目的黑色節點這一原則,較難維護!

enum Color
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	K _key;
	V _value;
	Color _color;	//顏色
	RBTreeNode<K, V>* _left;		//指向左孩子的指針
	RBTreeNode<K, V>* _right;	//指向右孩子的指針
	RBTreeNode<K, V>* _parent;	//指向父節點的指針

	RBTreeNode(const K& key=K(), const V&value=V())
		:_key(key)
		, _value(value)
		, _color(RED)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
	{}
};

  紅黑樹須要變色或利用旋轉來下降高度的幾種狀況:

圖注:g表明grandfather祖父節點;p表明parent父親結點;u表明uncle叔叔節點;cur表明當前節點

1、父節點是祖父節點的左孩子

1.uncle的顏色是紅色

①當前節點cur是parent的左孩子

②當前節點cur是parent的右孩子

 

2.uncle的顏色是黑色 或者 uncle爲NULL

①cur是parent的左孩子,右單旋

②cur是parent的右孩子,先左後右雙旋

2、父節點是祖父節點的右孩子

1.uncle的顏色是紅色

①cur是parent的右孩子

②cur是parent的左孩子

2.uncle的顏色是黑色 或者 uncle爲NULL

①cur是parent的右孩子

②cur是parent的左孩子

插入節點:

  1. 首先,要找到插入該節點的位置,找到後就插入節點
  2. 而後,對紅黑樹的節點顏色的合法性進行檢查,並根據檢查結果進行變色或者旋轉。

基於以上的狀況,紅黑樹利用模板類封裝的插入函數算法就完成了:

template<class K, class V>
bool RBTree<K, V>::Insert(const K& key, const V& value)
{
	if (_root == NULL)
	{
		_root = new RBTreeNode<K, V>(key, value);
		_root->_color = BLACK;
		return true;
	}
	//	找位置
	RBTreeNode<K, V>* cur = _root;
	RBTreeNode<K, V>* parent = NULL;
	while (cur)
	{
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			return false;
		}
	}
	//插入
	cur = new RBTreeNode<K, V>(key, value);
	cur->_parent = parent;
	if (parent->_key > key)
		parent->_left = cur;
	else if (parent->_key < key)
		parent->_right = cur;
	
	//檢查顏色分配是否知足要求
	while (parent&&parent->_color==RED)
	{
		RBTreeNode<K, V>* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			RBTreeNode<K, V>* uncle = grandfather->_right;
			if (uncle&&uncle->_color == RED)
			{	//第一種狀況  變色
				grandfather->_color = RED;
				parent->_color =BLACK;
				uncle->_color = BLACK;
				
				cur = grandfather;
				parent = grandfather->_parent;
			}
			else if( (uncle&&uncle->_color==BLACK)||uncle==NULL)
			{	
				if (cur == parent->_left)
				{//第二種狀況 右單旋		cur必然有黑色孩子
					parent->_color = BLACK;
					grandfather->_color = RED;
					RotateR(grandfather);
				}
				else
				{//第三種狀況 左右雙旋
					RotateL(parent);
					parent->_color = BLACK;
					grandfather->_color = RED;
					RotateR(grandfather);
				}
				break;
			}
		}
		else if (parent == grandfather->_right)
		{
			RBTreeNode<K, V>* uncle = grandfather->_left;
			if (uncle&&uncle->_color == RED)
			{//第一種狀況 變色
				grandfather->_color = RED;
				parent->_color = BLACK;
				uncle->_color = BLACK;
				
				cur = grandfather;
				parent = cur->_parent;
			}
			else if( (uncle&&uncle->_color == BLACK)||uncle==NULL)
			{//第二種狀況 左單旋 cur必然有黑孩子
				if (cur == parent->_right)
				{
					parent->_color = BLACK;
					grandfather->_color = RED;
					RotateL(grandfather);
				}
				else if (cur==parent->_left)
				{//第三種狀況 右左雙旋
					RotateR(parent);
					parent->_color = BLACK;
					grandfather->_color = RED;	
					RotateL(grandfather);
				}
				break;
			}
		}
	}
	_root->_color = BLACK;
	return true;
}

  插入完成以後,咱們沒法直觀的看出紅黑樹的節點顏色是否合法,也沒法直觀的看出每條路徑的黑色節點數目是否相同。

因此,這裏實現兩個函數,方便檢驗紅黑樹的合法性。

  • 紅黑樹每條路徑的黑色節點數目都相同,因此隨意遍歷一條路徑,計算這條路上的黑色節點的數目。以該數據爲標杆,和其餘路徑的黑色節點數目做比較,判斷是否都相同。
  • 若是當前節點是紅顏色而且它有父節點,那麼再判斷父節點的顏色是否也是紅色,這樣就能判斷該樹是否知足連續兩個節點不能同時爲紅色這一性質。
//檢驗紅黑樹的合法性
template<class K, class V>
bool RBTree<K, V>::Check()
{
	//統計紅黑樹每條路徑上黑色節點的數量
	int blackNum = 0;
	RBTreeNode<K, V>* cur = _root;
	while (cur)
	{
		if (cur->_color == BLACK)
			blackNum++;
		cur = cur->_left;
	}
	int CBNum = 0;
	return _Check(_root,blackNum,CBNum);
}

////////////////// 遞歸輔助
template<class K, class V>
bool RBTree<K, V>::_Check(RBTreeNode<K, V>* root, int blackNum, int CBNum)
{
	if (root == NULL)
		return true;
	if (root->_color == BLACK)
	{
		CBNum++;
		if (root->_left == NULL&&root->_right == NULL)
		{	//走到了葉子節點 將該條路徑上的黑色節點數量與以前統計的黑色節點數量作比較
			if (blackNum == CBNum)
			{
				return true;
			}
			else
			{
				cout << "葉子節點爲" << root->_key << "路徑的黑色節點數目與最左側支路的黑色節點數目不相等 !" << endl;
				return false;
			}
		}
	}
	else if (root->_parent&&root->_parent->_color == RED)
	{//判斷是否存在連續的兩個紅色節點
		cout << root->_parent->_key << " 和 " << root->_key << " 爲兩個連續的紅色節點" << endl;
		return false;
	}
	//遞歸檢驗子路
	return _Check(root->_left, blackNum, CBNum) && _Check(root->_right, blackNum, CBNum);
}

  紅黑樹和AVL樹的比較

紅黑樹和AVL樹都是高效的平衡二叉樹,增刪查改的時間複雜度都是O(lg(N)) 紅黑樹的不追求徹底平衡,保證最長路徑不超過最短路徑的2倍,相對而言,下降了旋轉的要求,因此性能會優於AVL樹,因此實際運用 中紅黑樹更多。

github紅黑樹代碼連接

相關文章
相關標籤/搜索