【資料結構】紅黑樹(如何實現及怎樣判斷)
紅黑樹的規則:
1、每個節點不是紅色就是黑色的。
2、根結點是黑色的。
3、如果一個節點是紅色的,則它的兩個子結點是黑色的。即每條路徑上不能存在兩個連續的紅節點。
4、對每個節點,從該節點到其他節點的簡單路徑上,均包含相同數目的黑色節點。
紅黑樹節點RBTreeNode的實現,利用三叉鏈(left、right、parent)、key、value及顏色col。
enum colour { RED, BLACK, }; template<class K, class V> struct RBTreeNode { RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; K _key; V _value; colour _col; RBTreeNode(const K& key = K(), const V& value = V()) :_left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _value(value) , _col(RED)//初始化插入節點的顏色為紅色,不影響黑節點個數 {} };
一、紅黑樹的插入
紅黑樹的插入類似於二叉搜尋樹,但是每次插入後都到要注意是否滿足紅黑樹的規則,特別是規則3和規則4。如果不滿足就需要調整樹的結構,下面對插入節點時分成以下幾種情況:
注:cur(插入節點)parent(cur的父親節點)grandfather(parent的父親節點)uncle(父親的兄弟節點)
1、根節點root為空,直接插入新節點並給root,設定根節點的顏色為BLACK。
2、根節點root不為空,找到插入節點的位置並插入節點cur。cur節點是紅色,若parent是紅節點,則需要進行調整。
存在以下三種情況:
情況一:cur為紅,parent為紅,grandfather為黑,uncle存在且為紅。
調整方案,如下如所示:
情況二:cur為紅,parent為紅,grandfather為黑,uncle不存在或者uncle為黑。
左單旋:parent為grantfather的右孩子,cur為parent的右孩子。
右單旋: parent為grantfather的左孩子,cur為parent的左孩子。
調整方案,下面以右單旋進行分析,如下圖所示:
左單旋轉類似右單旋轉的實現過程。
情況三:cur為紅,parent為紅,grantfather為黑,uncle不存在或者uncle為黑。
左右單旋:parent為grantfather的右孩子,cur為parent的左孩子。
右左單旋:parent為grantfather的左孩子,cur為parent的右孩子。
調整方案,下面以做右單旋進行分析,如下圖所示:
右左旋轉的實現類似左單旋轉的實現過程。
具體實現如下:
RBTree()
:_root(NULL)
{}
bool Insert(const K& key, const V& value)
{
//1、_root為空時,插入節點是根節點
if (_root == NULL)
{
_root = new Node(key, value);
_root->_col = BLACK;
return true;
}
Node* parent = NULL;
Node* cur = _root;
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 Node(key, value);//插入節點
if (parent->_key > cur->_key)
{
parent->_left = cur;
cur->_parent = parent;
}
if (parent->_key < cur->_key)
{
parent->_right = cur;
cur->_parent = parent;
}
//2、出現兩個連續紅節點,進行調整
while (cur != _root && parent->_col == RED)//此條件說明存在parent節點,parent存在父親節點
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)//情況一:uncle為RED
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
}
else //情況二、三:uncle為BLACK或不存在(右單旋或左右單旋)
{//先考慮左右單旋,先進行左單旋,轉化為情況二,再進行右單旋
if (cur == parent->_right)
{
_RotateL(parent);//左旋不需要變顏色
}
_RotateR(grandfather);
}
}
else
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_right)
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)//情況一:uncle為RED
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
}
else //情況二、三:uncle為BLACK或不存在(左單旋或右左單旋)
{
if (cur == parent->_left)
{
_RotateR(parent);//右旋不需要變顏色
}
_RotateL(grandfather);
}
}
}
cur = grandfather;
parent = cur->_parent;
_root->_col = BLACK;
}
}
void _RotateL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
parent->_right = SubRL;
if (SubRL)
SubRL->_parent = parent;
SubR->_parent = parent->_parent;
SubR->_left = parent;
parent->_parent = SubR;
//變色
SubR->_col = BLACK;//情況二中:parent變黑,grandfather變紅
parent->_col = RED;
parent = SubR;
if (parent->_parent == NULL)
_root = parent;
else
{
Node* ppNode = parent->_parent;
if (ppNode->_key > parent->_key)
ppNode->_left = parent;
else
ppNode->_right = parent;
}
}
void _RotateR(Node* parent)
{
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
parent->_left = SubLR;
if (SubLR)
SubLR->_parent = parent;
SubL->_parent = parent->_parent;
SubL->_right = parent;
parent->_parent = SubL;
//變色
SubL->_col = BLACK;//情況二中:parent變黑,grandfather變紅
parent->_col = RED;
parent = SubL;
if (parent->_parent == NULL)
_root = parent;
else
{
Node* ppNode = parent->_parent;
if (ppNode->_key > parent->_key)
ppNode->_left = parent;
else
ppNode->_right = parent;
}
}
二、紅黑樹的判斷
1、根結點是否滿足紅黑樹規則,是否為黑色。
2、每條路徑的黑色節點相等。統計出一條路徑的黑色節點的個數,然後與其他路徑黑色節點個數進行比較。
3、不存在連續的紅色節點,判斷紅色節點的父親節點是否為紅色。
具體實現如下:
bool Check()
{
if (_root->_col == RED)
return false;
int count = 0;//統計出一條路徑的黑色節點的個數
int num = 0;//需要與count比較的其他路徑黑色節點個數
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
count++;
cur = cur->_left;
}
return _Check(_root, count, num);
}
bool _Check(Node* root, int BlackNum, int CurBlackNum)
{
if (root == NULL)
return true;
if (root->_col == RED && root->_parent->_col == RED)//存在兩個連續的紅節點
return false;
if (root->_col == BLACK)//黑節點就CurBlackNum++
CurBlackNum++;
if (root->_left == NULL && root->_right == NULL)
{
if (CurBlackNum == BlackNum)
return true;
else//黑色節點不相等返回false
return false;
}
return _Check(root->_left, BlackNum, CurBlackNum)
&& _Check(root->_right, BlackNum, CurBlackNum);//進行左右遞迴
}
紅黑樹的效率:
1、最壞情況下,紅黑樹高度不超過2lgN
最壞的情況就是,紅黑樹中除了最左側路徑全部是由3-node節點組成,即紅黑相間的路徑長度是全黑路徑長度的2倍。
2、紅黑樹的平均高度大約為lgN
紅黑樹的運用(高效的二叉搜尋樹)
紅黑樹這種資料結構應用十分廣泛,在多種程式語言中被用作符號表的實現,如:
- Java中的java.util.TreeMap,java.util.TreeSet
- C++ STL中的:map,multimap,multiset
紅黑樹和AVL樹的比較
1、紅黑樹和AVL樹都是高效的平衡二叉樹,增刪查改的時間複雜度都是O(lg(N))
2、紅黑樹的不追求完全平衡,保證最長路徑不超過最短路徑的2倍,相對而言,降低了旋轉的要求,所以效能優於AVL樹,所以實際運用中紅黑樹更多。紅黑樹是一種特殊的二叉查詢樹,他的查詢方法也和二叉查詢樹一樣,不需要做太多更改,但是由於紅黑樹比一般的二叉查詢樹具有更好的平衡,所以查詢起來更快。
以上為個人學習的一些總結,如有紕漏,請多多指教。
相關推薦
【資料結構】紅黑樹(如何實現及怎樣判斷)
紅黑樹是一顆二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是red或black。通過對任何一條從根節點到葉子節點的簡單路徑上的顏色來約束,紅黑樹保證了最長路徑不超過最短路經的兩倍,因此近似於平衡。 紅黑樹的規則: 1、每個節點不是紅色就是
【資料結構】 紅黑樹
一、概念 Red-Black Tree 簡稱 R-B Tree,是一種自平衡二叉查詢樹,是在電腦科學中用到的一種資料結構,典型的用途是實現關聯陣列。 二、特性 (1)每個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每個葉子節點(NIL)是黑
【資料結構】紅黑樹的插入(Insert)
前言: 紅黑樹是一棵二叉搜尋樹,它在每個節點上增加了一個儲存位來表示節點的顏色,可以是Red或Black。通過對任何一條從根到葉子簡單路徑上的顏色來約束,紅黑樹保證最長路徑不超過最短路徑的兩倍,因而近似於平衡。 紅黑樹的基本概念: 紅黑樹是滿足下面紅黑性質的二叉
【資料結構】紅-黑樹
1.紅-黑樹的特徵 它主要有兩個特徵:1.節點都有顏色;2.在插入和刪除的過程中,要遵循保持這些顏色的不同排列的規則。首先第一個特徵很好解決,在節點類中店家一個數據欄位,例如boolean型變數,以此來表示節點的顏色資訊。第二個特徵比較複雜,紅-黑樹有它
【資料結構】二叉樹的構建及遍歷(遞迴演算法)
題目描述: 編一個程式,讀入使用者輸入的一串先序遍歷字串,根據此字串建立一個二叉樹(以指標方式儲存)。 例如如下的先序遍歷字串: ABC##DE#G##F### 其中“#”表示的是空格,空格字元代表空樹。建立起此二叉樹以後,再對二叉樹進行中序遍歷,輸出遍歷結果。 具體程式
linux核心分析--核心中的資料結構之紅黑樹(續)
#include<linux/rbtree.h> #include <linux/string.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); struct student { int id;
linux核心分析--核心中的資料結構之紅黑樹(四)
紅黑樹由於節點顏色的特性,保證其是一種自平衡的二叉搜尋樹。 紅黑樹的一系列規則雖然實現起來比較複雜,但是遵循起來卻比較簡單,而且紅黑樹的插入,刪除效能也還不錯。 所以紅黑樹在核心中的應用非常廣泛,掌握好紅黑樹,即有利於閱讀核心原始碼,也可以在自己的程式碼中借鑑這種資料結構。 紅黑樹必
資料結構之紅黑樹(二)——插入操作
插入或刪除操作,都有可能改變紅黑樹的平衡性,利用顏色變化與旋轉這兩大法寶就可應對所有情況,將不平衡的紅黑樹變為平衡的紅黑樹。 在進行顏色變化或旋轉的時候,往往要涉及祖孫三代節點:X表示操作的基準節點,P代表X的父節點,G代表X的父節點的父節點。 我們先來大體預覽一下插入的
【資料結構】二叉樹(順序儲存、鏈式儲存)的JAVA程式碼實現
二叉樹是一種非線性的資料結構。它是由n個有限元素的集合,該集合或者為空、或者由一個稱為根(root)的元素及兩顆不相交的、被分別稱為左子樹、右子樹的二叉樹組成。當集合為空時,稱該二叉樹為空二叉樹。在二叉樹中,一個元素也可以稱做一個結點。二叉樹是有序的,即若將其左右兩個子樹顛倒
【資料結構】二叉樹的實現
上篇部落格中,我們詳細說明了樹和二叉樹的資料結構及其特徵,本次,我們用C++來實現一下二叉樹 定義二叉樹節點結構 二叉樹需要定義指向左孩子和右孩子節點的指標,還有儲存的資料;我們在這把它的建構函式也寫出來 //定義一個二叉樹節點 template<typename
【資料結構】圖的構建(鄰接表法)
#include<iostream> #include<string> #include<queue> using namespace std; #define ERROR 1 #define MAX_VERTEX_NUM 100 typedef struct ArcNod
【資料結構】資料結構探索(四)—— 紅黑樹(R-B Tree)
紅黑樹,一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。 紅黑樹有五個性質: 性質1. 節點是紅色或黑色。 性質2. 根節點是黑色。 性質3 每個葉節點(NIL節點,空節點)是黑色的。 性質4 每
【資料結構】二叉樹的相關操作(待更)
#include "stdio.h" #include "stdlib.h" typedef struct node { char data; struct node *rchild,*lchild; }bintnode; typedef bintnode *bintree;//指向該結構體
【資料結構】二叉樹的建立和遍歷(非遞迴)
該程式使用的是遞迴地建立方法,以及非遞迴的遍歷演算法 執行環境:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild
【資料結構】二叉樹的建立與遍歷(遞迴)
該程式全是使用遞迴的操作 執行環境是:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild,*rchild; }bi
資料結構學習筆記------紅黑樹(附c++程式碼)
1、紅黑樹簡介 紅黑樹是二叉查詢樹的一種,其增刪改查的統計效能要優於AVL樹,查詢、插入、刪除演算法的複雜度都為O(log(n))。先附上紅黑樹這種資料結構的性質: 性質1、節點是紅色或黑色。 性質2、根節點是黑色。 性質3、每個葉節點(是指的空節點,nil節點)是黑色的。 性質4、
【演算法】紅黑樹(二叉樹)概念與查詢(一)
誒,演算法這個東西,其實沒那麼簡單,但是也沒那麼難。 紅黑樹,其實已經有很多大佬都整理過了,而且文章部落格都寫得超好,我寫這篇文章的目的是:自己整理一次,這些知識才是自己的,否則永遠是別人的~ 該系列到現在暫只有3篇文章: 【演算法】紅黑樹(二叉樹)概念與查詢(一):h
資料結構與演算法:紅黑樹(Red Black Tree)
一、簡介 紅黑樹(Red Black Tree)是一棵二叉查詢樹,在每個節點增加一個屬性表示節點顏色,值為紅色(Red)或者黑色(Black)。紅黑樹也是“平衡”樹中的一種,通過對任何一條從根到葉子的路徑上各個節點的顏色來進行約束,確保沒有一條路徑會比其他
資料結構——紅黑樹(red-black tree)
RB-tree(紅黑樹)是一種平衡二叉搜尋樹,它每個節點上增加了一個儲存位來表示節點的顏色,可以是 Red 或 Black,故得名。通過對任何一條從根到葉子的簡單路徑上各個節點的顏色進行約束,紅黑樹能夠確保沒有一條路徑會比其他路徑長出 2 倍,近似於平衡。
【資料結構】---------二叉樹面試題(具體的所有實現)
實現二叉樹的相關的操作: 先序遍歷樹(遞迴) 中序遍歷樹(遞迴) 後序遍歷樹(遞迴) 層序遍歷樹 建立一棵樹 樹的銷燬 樹的拷貝 二叉樹中節點的個數 二叉樹葉子節點的個數 二叉樹第K層節點的個數 樹的高度 在二叉樹中查詢節點 找當前節點的左子樹