1. 程式人生 > >平衡搜尋樹--紅黑樹 RBTree

平衡搜尋樹--紅黑樹 RBTree

紅黑樹是一棵二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是RedBlack

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

紅黑樹是滿足下面紅黑性質的二叉搜尋樹:

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

2. 根節點是黑色的

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

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

思考:為什麼滿足上面的顏色約束性質,紅黑樹能保證最長路徑不超過最短路徑的兩倍?

  最短的路徑上節點的顏色全部都為黑色;最長的路徑則為黑紅交叉的路徑,其上有與最短路徑的黑節點數目相同的黑節點數和紅節點數目。所以我們按照紅黑樹性質所建立的紅黑樹的最長路徑必然不會超過最短路徑的兩倍!

建立紅黑樹的節點類:

插入的新節點預設是紅色的。原因是:插入黑節點必然會影響所有路徑都含有相同數目的黑色節點這一原則,較難維護!

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.uncle的顏色是紅色

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

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

 

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

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

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

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

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樹,所以實際運用 中紅黑樹更多。

相關推薦

平衡搜尋-- RBTree

紅黑樹是一棵二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是Red或Black。 通過對任何一條從根到葉子節點簡單路徑上的顏色來約束樹的高度,紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似於平衡。 紅黑樹是滿足下面紅黑性質的二叉搜尋樹: 1. 每個節點,不是紅色就是黑色的 2.

關於的總結從二叉->二叉搜尋->平衡二叉->->B與B+

二叉樹的定義與性質,包括各種操作的原始碼在本部落格的的此處:二叉樹 二叉搜尋樹(Binary Search Tree)的定義性質以及原始碼實現在本部落格此處:二叉搜尋樹 平衡二叉樹(AVL樹),是一棵

平衡——

平衡樹:左右子樹的樹的高度相差不超過1。 紅黑書使得時間複雜度大大降低,從O(N)到O(logN)。 紅黑規則 1、每一個節點不是紅色就是黑色 2、根點總是黑色 3、如果節點是紅色,則它的子節點必須是黑色(反之不一定為真) 4、從根到葉節點或空子節點的每條路徑,必須包含相同的黑色節點。

最小堆/雜湊表/二叉/平衡二叉/的意義

接觸堆資料結構是在排序裡面講的,空間複雜度O(1),時間複雜度O(NlogN),但是在實踐中還是不如快速排序(好像快速排序可以更好的利用硬體特性)。堆 的意義就在於:最快的找到最大/最小值,在堆結構中插入一個值重新構造堆結構,取走最大/最下值後重新構造堆結構 其時間複雜度為O(logN),而其他方法最少為O

平衡查詢——2-3查詢——(1)

二叉查詢樹VS平衡查詢樹 Binary Search Tree possibilities Balanced Search Trees 如何學 平衡查詢樹 為了保證之前學習的二分查詢樹BST的平衡性,解決在最壞情況時高度為N的情況

數據結構學習筆記-排序/隊/棧/鏈/堆/查找/

算法 數據結構排序:插入排序:每次從剩余數據中選取一個最小的,插入已經排序完成的序列中合並排序:將數據分成左右兩組分別排序,然後合並,對每組數據的排序遞歸處理。冒泡排序:重復交換兩個相鄰元素,從a[1]開始向a[0]方向冒泡,然後a[2]...當a[i]無法繼續往前擠的時候說明前面的更小了,而且越往前越小(擠

霍夫曼 二三 B B+

傾斜 節點 存在 ecn nbsp rod htm com 霍夫曼樹 霍夫曼樹: 特點:帶權路徑長度最短,∑(每個節點的權重)*(每個節點的層數) 生成:每次合並權值最小的兩個節點(子樹)建立二叉樹,將合並後的子樹作為新節點,權值為節點(子樹)權值之和 二三樹: 特

2-3 /(red-black tree)

https ret html 技術分享 turn nfc font 進行 sre 2-3 tree 2-3樹節點: null節點,null節點到根節點的距離都是相同的,所以2-3數是平衡樹 2叉節點,有兩個分樹,節點中有一個元素,左樹元素更小,右樹元素節點更大 3叉節點

基礎集合

  紅黑樹是什麼?要說到紅黑樹,就不得不說二叉排序樹(Binary Sort Tree,又稱二叉查詢樹或二叉搜尋樹)。  一、二叉排序樹(二叉查詢樹)   二叉排序樹都滿足下列性質:   (1)若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值;   (2)若右子樹不空,

2-3 /(red-black tree)學習筆記

2-3 tree 2-3樹節點: null節點,null節點到根節點的距離都是相同的,所以2-3數是平衡樹 2叉節點,有兩個分樹,節點中有一個元素,左樹元素更小,右樹元素節點更大 3叉節點,有三個子樹,節點中有兩個元素,左樹元素更小,右樹元素更大,中間樹介於兩個父元素之間。 插入操作如下圖所示 紅

二叉之BAVL堆積、B-、B+

B樹        即二叉搜尋樹:        1.所有非葉子結點至多擁有兩個子節點(Left和Right);        2.所有結點儲存一個關鍵字;        3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;        如

二叉之BAVL堆積、B-、B+總結分析

B樹        即二叉搜尋樹:        1.所有非葉子結點至多擁有兩個兒子(Left和Right);        2.所有結點儲存一個關鍵字;        3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;        如:        B樹的搜尋,從根結點開

淺談AVL,,B,B+原理及應用

背景:這幾天在看《高效能Mysql》,在看到建立高效能的索引,書上說mysql的儲存引擎InnoDB採用的索引型別是B+Tree,那麼,大家有沒有產生這樣一個疑問,對於資料索引,為什麼要使用B+Tree這種資料結構,和其它樹相比,它能體現的優點在哪裡? 看完這篇

資料結構 JAVA描述(十六) 動態查詢 B- B+

B-樹 前面介紹的查詢演算法都在記憶體中進行的,它們適合用於較小的檔案,而對於較大的、存放在外存的檔案就不合適,對於此類較大規模的檔案,即使是採用了平衡二叉樹,在查詢效率上仍然較低。例如若將存放在外存的10億條記錄組織為平衡二叉樹,則每次訪問記錄需要進行約30

B、B-、B+、B*

B樹 即二叉搜尋樹:        1.所有非葉子結點至多擁有兩個兒子(Left和Right);        2.所有結點儲存一個關鍵字;        3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹; 如:        B樹的搜尋,從根結點開始,如果查詢的關鍵字與結點的

BST,AVL,,B,B+,B*(從map的底層實現到mysql索引原理)

本文不會對具體細節過多的探究,力求得到這幾種樹的聯絡以及區別,實際運用。 BST(二叉檢索樹): 二叉檢索樹也是我們最熟悉的一個索引方式了,它保證所有節點的左子樹都小於該節點,所有節點的右子樹都大於該節點。就可以通過大小比較關係來進行快速的檢索,在一棵滿二叉

B和B+

首先,B樹的建立就是為了優化資料庫查詢,如果採用二叉查詢樹(時間複雜度只要LogN)來進行查詢,那麼在磁碟進行I/O操作時,(資料太大需要進行分頁)每個磁碟頁對應一個節點;最壞情況:查詢次數等於輸的高度(時間複雜度LogN), 自頂向下查詢10:需要4次 那這樣的

----插入和刪除結點的全程演示

引言:     目前國內圖書市場上,抑或網上講解紅黑樹的資料層次不齊,混亂不清,沒有一個完整而統一的闡述。而本人的紅黑樹系列四篇文章(詳見文末的參考文獻),雖然從頭至尾,講的有根有據,層次清晰,然距離讀者真正做到紅黑樹瞭然於胸,則還缺點什麼。     而我們知道,即便在經典的演算法導論一書上,也沒

資料結構之--二叉/B/B+/及相關演算法

樹 前言:以下所有概念來自教材、演算法導論或其他權威資料,如有記錄出錯,歡迎指正 定義 樹是一種非線性的資料結構 樹是若干個結點的集合(個數>=0),是由唯一的根和若干棵互不相交的子樹組成 樹的結點樹可以為0,對於這種樹,我們稱為空樹 樹與圖的區別

資料結構中常見的(BST二叉搜尋、AVL平衡二叉、RBT、B-、B+、B*

BST樹 即二叉搜尋樹:        1.所有非葉子結點至多擁有兩個兒子(Left和Right);        2.所有結點儲存一個關鍵字;        3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹; 如: