紅黑樹詳解

1.爲何須要紅黑樹?html

對於二叉搜索樹,若是插入的數據是隨機的,那麼它就是接近平衡的二叉樹,平衡的二叉樹,它的操做效率(查詢,插入,刪除)效率較高,時間複雜度是O(logN)。可是可能會出現一種極端的狀況,那就是插入的數據是有序的(遞增或者遞減),那麼全部的節點都會在根節點的右側或左側,此時,二叉搜索樹就變爲了一個鏈表,它的操做效率就下降了,時間複雜度爲O(N),因此能夠認爲二叉搜索樹的時間複雜度介於O(logN)和O(N)之間,視狀況而定。那麼爲了應對這種極端狀況,紅黑樹就出現了,它是具有了某些特性的二叉搜索樹,能解決非平衡樹問題,紅黑樹是一種接近平衡的二叉樹。node

2.紅黑樹的特性有哪些?spa

首先,紅黑樹是一個二叉搜索樹,它同時知足如下特性:3d

(1) 每一個節點要麼是黑色,要麼是紅色code

(2) 根節點是黑色htm

(3) 若是節點是紅色的,那麼它的子節點必須是黑色的(反之,不必定須要成立)blog

(4) 從根節點到葉節點或空子節點的每條路徑,都包含相同數目的黑色節ip

經過看圖來理解以上四個特性get

 

3.紅黑樹的效率io

紅黑樹的查找,插入和刪除操做,時間複雜度都是O(logN)。查找操做時,它和普通的相對平衡的二叉搜索樹的效率相同,都是經過相同的方式來查找的,沒有用到紅黑樹特有的特性。但,若是插入的時候是有序數據,那麼紅黑樹的查詢效率就比二叉搜索樹要高了,由於此時二叉搜索樹不是平衡樹,它的時間複雜度O(N)。插入和刪除操做時,因爲紅黑樹的每次操做平均要旋轉一次和變換顏色,因此它比普通的二叉搜索樹效率要低一點,不過期間複雜度仍然是O(logN)。總之,紅黑樹的優勢就是對有序數據的查詢操做不會慢到O(logN)的時間複雜度。

4.對旋轉的理解

在紅黑樹中,插入或者刪除數據時,爲了保持紅黑樹的那五個特性,須要進行旋轉和變換顏色的操做。旋轉必需要一次性作兩件事情:

* 使一些節點上升,一些節點降低,幫助樹平衡

* 保證不破壞二叉搜索樹的特徵

旋轉分爲左旋轉和右旋轉,那麼咱們就看下左旋轉和右旋轉是怎麼回事。

4.1 左旋轉

此處,以50爲支點進行逆時針旋轉,而後75成爲了頂點,50成爲了75的左子節點,65成爲了50的右子節點,這個操做就是左旋轉。

4.2 右旋轉

此處,以75爲支點順時針旋轉,而後50成爲了頂點,75成爲了50的右子節點,65成爲了75的左子節點,這就是右旋轉操做。

5.插入操做

在介紹插入操做前,先約定一下各個節點的名稱。看圖:

在紅黑樹中,插入一個節點,都執行了哪些操做呢?

首先,查找要插入節點的位置,而後給予顏色(紅色或者黑色),可是爲了保證紅黑樹的特性,須要進行旋轉或者更改顏色,須要注意的是,在旋轉前和旋轉後,紅黑樹一直都是一個二叉搜索樹,二叉搜索樹的特徵從未改變過。

這裏,對於新插入的節點,咱們給予的顏色是紅色,是由於爲了和紅黑樹的第(4)條特性不衝突: 從根節點到葉節點或空子節點的每條路徑,都包含相同數目的黑色節點。這樣就少了不少操做。而後看其它幾個特性

* 對於(1)特性:每一個節點要麼是黑色,要麼是紅色,不衝突。

* 對於(2)特性:根節點是黑色,若是插入的節點不是根節點,也不衝突。若是是根節點,那麼直接給予顏色黑色

那麼,惟一須要知足的特性就是(3):若是節點是紅色的,那麼它的子節點必須是黑色的。這裏,當咱們給予新插入的節點顏色是紅色時,須要根據父節點的不一樣狀況作不一樣的處理,以知足這個特性。有如下幾種狀況:

根據代碼來理解這幾種狀況:

/* 紅黑樹修正程序 */
void insert_repair_tree(struct node* n) {
//狀況一:節點N的父節點爲null,說明該節點是根節點
if (parent(n) == NULL) { insert_case1(n); } else if (parent(n)->color == BLACK) {
//狀況二:父節點是黑色 insert_case2(n); }
else if (uncle(n)->color == RED) {
//狀況三: 父節點和叔叔節點都是紅色 insert_case3(n); }
else {
//狀況四:父節點是紅色,叔叔節點是黑色 insert_case4(n); } }

5.1:若是新插入節點是根節點,那麼給予該節點顏色是黑色

看代碼:

 

/* 狀況一:插入的節點是根節點 */
void insert_case1(struct node* n)
{
 if (parent(n) == NULL)
  n->color = BLACK;
}

 

5.2:若是新插入節點的父節點是黑色,那麼給予該節點是紅色不會對該紅黑樹有任何影響,因此不作任何處理。

看代碼:

/* 狀況二:父節點是黑色 */
void insert_case2(struct node* n)
{
  return; /* Do nothing since tree is still valid */
}

5.3:若是新插入節點的父節點是紅色,同時叔叔節點也是紅色

       須要將父親節點和叔叔節點從新繪製爲黑色,祖父節點繪製爲紅色。可是,若是此處祖父節點爲根節點,那麼須要調用紅黑樹修正程序,將祖父節點繪製爲黑色。

看代碼:發現看代碼比看圖要更容易理解,而且邏輯也更嚴謹,對於父親節點和叔叔節點都是紅色時,發現其餘文章都沒有考慮祖父節點爲根節點的狀況,這裏圖就省了。

/* 狀況三:父節點和叔叔節點都是紅色 */
void insert_case3(struct node* n)
{
 parent(n)->color = BLACK;
 uncle(n)->color = BLACK;
 grandparent(n)->color = RED;
//調用紅黑樹修正程序 insert_repair_tree(grandparent(n)); }

5.4:若是新插入節點的父節點是紅色,同時叔叔節點是黑色

看代碼:

/* 狀況四:第一步 */
void insert_case4(struct node* n)
{
 struct node* p = parent(n);
 struct node* g = grandparent(n);

 if (n == g->left->right) {
  /* 若是父節點是祖父節點的左子節點,新插入節點是父節點的右子節點,那麼以父節點爲支點左旋 */
  rotate_left(p);
  n = n->left;
 } else if (n == g->right->left) {
    /* 若是父節點是祖父節點的右子節點,新插入節點是父節點的左子節點,那麼以父節點爲支點右旋 */
  rotate_right(p);
  n = n->right; 
 }

 insert_case4step2(n);
}
/* 狀況四:第二步 */
/* 在第二步時:當前節點n確定處於 祖父節點左子節點的左側,或者祖父節點右子節點的右側 */ void insert_case4step2(struct node* n) {
struct node* p = parent(n); struct node* g = grandparent(n); if (n == p->left)
/* 以祖父節點爲支點進行右旋 */ rotate_right(g);
else rotate_left(g); p->color = BLACK; g->color = RED; }

在知足父節點是紅色,叔叔節點是黑色的條件下,咱們能夠分如下幾種狀況:

狀況(a):父節點p是祖父節點g的左子節點,新插入節點n是父節點p的右子節點

處理:以父節點爲支點左旋,而後以祖父節點爲支點進行右旋,最後給父節點(第一次旋轉後的父節點)繪製爲黑色,給祖父節(第一次旋轉後的祖父節點)點繪製爲紅色

看圖:

狀況(b):父節點p是祖父節點g的右子節點,新插入節點n是父節點p的左子節點

處理:以父節點爲支點進行右旋,而後以祖父爲支點進行左旋,最後給父節點(第一次旋轉後的父節點)繪製爲黑色,給祖父節(第一次旋轉後的祖父節點)點繪製爲紅色

看圖:

狀況(c):父節點p是祖父節點g的左子節點,新插入節點n是父節點p的左子節點

處理:以祖父節點爲支點進行右旋,最後給父節點繪製爲黑色,給祖父節點繪製爲紅色,也就是直接調用第二步的方法 insert_case4step2

狀況(d):父節點p是祖父節點g的右子節點,新插入節點n是父節點p的右子節點

處理:以祖父爲支點進行左旋,最後給父節點繪製爲黑色,給祖父節點繪製爲紅色,也就是直接調用第二步的方法 insert_case4step2

 6 刪除操做

對於刪除操做,處理的邏輯是:根據二叉搜索樹的特色找到被刪除的節點,將其刪除掉,而後再經過改變顏色和旋轉操做保持其紅黑樹的特性。此處咱們統一規定被刪除節點是D,真正被刪除節點爲RD,真正被刪除節點的兄弟節點是B,真正被刪除節點的父節點是P,B的兩個子節點是BL和BR。刪除操做有三種狀況:

6.1 被刪除節點爲葉子節點,分爲兩種狀況:

狀況(a):該葉子節點是紅色

處理:直接刪除

狀況(b):該葉子節點是黑色,

處理:刪除節點,而後旋轉和變換顏色。

6.2 被刪除節點只有一個子節點,那麼使被刪除節點的父節點指向被刪除節點的子節點,而後經過改變節點顏色和旋轉來保持紅黑樹特性

6.3 被刪除節點D有兩個子節點,那麼能夠將被刪除節點D的後繼節點(D的右子樹中的最小元素RD)的值賦值給被刪除節點D,可是被刪除節點D的顏色不作改變,此時刪除操做就轉化爲了刪除後繼節點RD的狀況了。那麼刪除RD的狀況又分爲五種:

狀況(a): RD節點是紅色的

處理:若是RD節點是紅色的,那麼它的父節點和右子節點應該是黑色的,當刪除RD節點時,直接把RD節點的父節點指向RD節點的右子節點便可。

下面四種狀況RD節點都是黑色的

狀況(b):RD的兄弟節點B爲紅色

處理:若是B節點爲紅色,那麼P節點和B的兩個子節點都是黑色。交換B節點和P節點的顏色,而後以P節點爲支點進行左旋轉。這樣處理後,就將狀況(b)轉爲(c),(d),(e)中的一種。

看圖:

 

 狀況(c):

 

後續更新。。。

 

 

參考資料:

模擬紅黑樹:https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

維基百科紅黑樹:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion

相關文章
相關標籤/搜索