紅黑樹和我們以前學過的AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,從而獲得較高的查找性能。不過自從紅黑樹出來後,AVL樹就被放到了博物館裡,據說是紅黑樹有更好的效率,更高的統計性能。這一點在我們了解了紅黑樹的實現原理後,就會有更加深切的體會。
紅黑樹和AVL樹的區別在於它使用顏色來標識結點的高度,它所追求的是局部平衡而不是AVL樹中的非常嚴格的平衡。學過數據結構的人應該都已經領教過AVL樹的復雜,但AVL樹的復雜比起紅黑樹來說簡直是小巫見大巫,紅黑樹才是真正的變態級數據結構。
由於STL中的關聯式容器默認的底層實現都是紅黑樹,因此紅黑樹對於後續學習STL源碼還是很重要的,有必要掌握紅黑樹的實現原理和源碼實現。
紅黑樹是AVL樹的變種,紅黑樹通過一些著色法則確保沒有一條路徑會比其它路徑長出兩倍,因而達到接近平衡的目的。所謂紅黑樹,不僅是一個二叉搜索樹,而且必須滿足一下規則:
1、每個節點不是紅色就是黑色。
2、根節點為黑色。
3、如果節點為紅色,其子節點必須為黑色。
4、任意一個節點到到NULL(樹尾端)的任何路徑,所含之黑色節點數必須相同。
上面的這些約束保證了這個樹大致上是平衡的,這也決定了紅黑樹的插入、刪除、查詢等操作是比較快速的。 根據規則4,新增節點必須為紅色;根據規則3,新增節點之父節點必須為黑色。當新增節點根據二叉搜索樹的規則到達其插入點時,卻未能符合上述條件時,就必須調整顏色並旋轉樹形,如下圖:
假設我們為上圖分別插入節點3、8、35、75,根據二叉搜索樹的規則,插入這四個節點後,我們會發現它們都破壞了紅黑樹的規則,因此我們必須調整樹形,也就是旋轉樹形並改變節點的顏色。
(1). 從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長。
根據上面的性質5我們知道上圖的紅黑樹每條路徑上都是3個黑結點。因此最短路徑長度為2(沒有紅結點的路徑)。再根據性質4(兩個紅結點不能相連)和性質1,2(葉子和根必須是黑結點)。那麼我們可以得出:一條具有3個黑結點的路徑上最多只能有2個紅結點(紅黑間隔存在)。也就是說黑深度為2(根結點也是黑色)的紅黑樹最長路徑為4,最短路徑為2。從這一點我們可以看出紅黑樹是 大致平衡的。 (當然比平衡二叉樹要差一些,AVL的平衡因子最多為1)
(2). 紅黑樹的樹高(h)不大於兩倍的紅黑樹的黑深度(bd),即h<=2bd
根據定理1,我們不難說明這一點。bd是紅黑樹的最短路徑長度。而可能的最長路徑長度(樹高的最大值)就是紅黑相間的路徑,等於2bd。因此h<=2bd。
(3). 一棵擁有n個內部結點(不包括葉子結點)的紅黑樹的樹高h<=2log(n+1)
下面我們首先證明一顆有n個內部結點的紅黑樹滿足n>=2^bd-1。這可以用數學歸納法證明,施歸納於樹高h。當h=0時,這相當於是一個葉結點,黑高度bd為0,而內部結點數量n為0,此時0>=2^0-1成立。假設樹高h<=t時,n>=2^bd-1成立,我們記一顆樹高 為t+1的紅黑樹的根結點的左子樹的內部結點數量為nl,右子樹的內部結點數量為nr,記這兩顆子樹的黑高度為bd'(注意這兩顆子樹的黑高度必然一 樣),顯然這兩顆子樹的樹高<=t,於是有nl>=2^bd'-1以及nr>=2^bd'-1,將這兩個不等式相加有nl+nr>=2^(bd'+1)-2,將該不等式左右加1,得到n>=2^(bd'+1)-1,很顯然bd'+1>=bd,於是前面的不等式可以 變為n>=2^bd-1,這樣就證明了一顆有n個內部結點的紅黑樹滿足n>=2^bd-1。
在根據定理2,h<=2bd。即n>=2^(h/2)-1,那麼h<=2log(n+1)
從這裡我們能夠看出,紅黑樹的查找長度最多不超過2log(n+1),因此其查找時間復雜度也是O(log N)級別的。
RB-TREE有紅黑二色,並且擁有左右子節點,因此,很容易勾勒出其結構風貌。由於在RB-TREE各種操作往往需要用到父節點,所以在數據結構中增加一個parent指針。下面是定義RBT的數據結構:
//定義紅黑樹的數據結構
typedef enum colorType{Red,Black} colorType;
typedef int elementType;
typedef struct RedBlackNode
{
struct RedBlackNode *parent; //父節點
struct RedBlackNode *Left; //指向左節點
struct RedBlackNode *Right; //指向右節點
colorType color; //j節點顏色,非黑即紅
elementType element; //節點存放的數據
}RBTree,*PRBTree;
下面是RB-TREE的節點圖標,其中element=10;
構建一顆空樹:
// 紅黑樹,包含一個指向根節點的指針
typedef struct RBTree
{
struct RedBlackNode* root;
}*RB_Tree;
// 紅黑樹的NIL節點
static struct RedBlackNode NIL = {0, 0, 0, 0, BLACK};
#define PNIL (&NIL) // NIL節點地址
void Init_RBTree(RB_Tree pTree) // 初始化一棵紅黑樹
{
pTree->root = PNIL;
}
1、插入時,AVL和RBT都最多需要2次旋轉;刪除時,AVL最多需要lgN次旋轉,而RBT最多需要3次旋轉;
2、RBT旋轉平衡時,需要變色操作,在O(lgN)數量級上,但操作簡單、速度快;
3、二者插入刪除的代價主要消耗在查找操作上,都與O(lgN)成正比;
研究表明:RBT的總體統計性能要好於平衡二叉樹。
紅黑樹現在應用很廣泛,任何鍵值對應,需要隨機存儲和鍵有序的情況都可以用;
比如:C++STL中的set, multiset, map, multimap等內存中比如緩存的(區塊-數據),編號對應內容,引索號對應數據項在Linux內核中,對虛擬內存的管理 ==