《算法導論》第十三章----紅黑樹

《算法導論》學習記錄目錄html

紅黑樹是"平衡的"二叉查找樹(PS: 關於二叉查找樹的內容見連接)。普通的二叉查找樹,在最壞狀況下基本操做的運行時間爲Θ(n),大部分的操做的最壞狀況運行時間與樹的高度成正比,對於相同的數據如何構造出一棵高效的二叉查找樹(即高度儘可能小)(以下圖)算法

圖a爲高效的二叉查找樹,圖b爲低效的二叉查找樹ide

 

形成圖b中的二叉查找樹低效的緣由在於建樹的時候結點插入的順序不恰當,選取關鍵字爲2的結點(最小關鍵字結點)做爲根結點使得全部的結點都位於根結點的右子樹裏。函數

更糟糕是全部的結點都在同一條路上,這時候就至關於在鏈表上進行操做。學習

可是普通的二叉查找樹不會自動調整,使本身的儘可能平衡(根結點的左右子樹的高度接近相同),它只會根據插入結點的關鍵字來判斷插入的位置。爲了使其能夠接近平衡,咱們能夠在結點裏添加一些屬性,而後根據這些屬性來動態調整樹的形狀。(例如紅黑樹添加了顏色屬性,AVL樹添加告終點高度屬性。)spa

紅黑樹的基本性質:3d

顏色屬性:顏色爲紅色(RED)或者黑色(BLACK)。指針

NIL結點:指向二叉查找數的外結點(葉子)指針,顏色爲黑色;某些結點的父結點或者子結點指向NIL結點,至關於哨兵。(即爲書中的nil[ T ])code

紅黑性質:一、每一個結點的顏色爲紅色或者黑色htm

     二、根結點的顏色爲黑色

     三、每一個葉結點都是黑色

        四、若是一個結點是紅色的,那麼它的兩個兒子都是黑色的

         五、對於每一個結點,從該結點到其子孫結點的全部路徑上包含相同數目的黑結點

黑高度:從該結點x出發(不包括該結點)到達葉結點的任意一條路徑上,黑色結點的個數爲該結點的黑色高度,用bh(x)

具體以下圖所示(深色爲黑色結點,淺色爲紅色結點):

下爲紅黑樹結點結構體、紅黑樹結構體和NIL指針等基本實現

 1 #define RED 1
 2 #define BLACK 0
 3 
 4 typedef struct RB_TreeNode {
 5     int value;
 6     int color;
 7     struct RB_TreeNode * parent;
 8     struct RB_TreeNode * left;
 9     struct RB_TreeNode * right;
10 }RB_TreeNode;                       //紅黑樹結點結構體
11 
12 typedef struct RB_Tree {
13     RB_TreeNode * root;
14 }RB_Tree;                           //紅黑樹結構體,根結點
15 
16 RB_TreeNode  NILL = { -1, BLACK, NULL, NULL, NULL };
17 RB_TreeNode *NIL = &NILL;                               //NIL結點指針
View Code

一棵n個內結點的紅黑樹的高度最多爲2lg(n+1),這是紅黑樹是一種好的查找樹的緣由。(PS:證實見書本,本人太菜,看完不會用本身的話總結。。。因此就不證實)

所以查找、插入、找最小值、找出最大值、找出前趨、找出後繼、刪除動態集合操做在紅黑樹上能夠在O(lgn)時間內完成。

結點的旋轉

由於直接會以前二叉查找樹的插入、刪除的實現不能保證操做後的二叉樹仍是紅黑樹,因此咱們添加旋轉操做,同過旋轉操做來使紅黑樹在插入、刪除操做後還能保持自己的性質。

旋轉分爲左旋轉、右旋轉(以下圖所示)

 

經過旋轉,咱們能夠修改結點的指針域使結點知足本身的需求。

下爲左旋轉、右旋轉的實現

 1 /*
 2  * x結點上進行左旋轉,y結點(x結點的右兒子)的左兒子成爲x結點的新右兒子
 3  * x結點成爲y結點的左兒子的新父結點
 4  * x結點的父結點成爲y結點的新父結點,y結點成爲x結點的新父結點
 5  * x結點成爲y結點的新左兒子
 6  */
 7 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){
 8     RB_TreeNode *y = x->right;       //x點右兒子          
 9     x->right = y->left;              //y結點的左兒子會成爲x結點的右兒子
10     if(y->left != NIL)
11         y->left->parent = x;         //若是y有左兒子,y的左兒子的父結點爲x
12     y->parent = x->parent;
13     if(x->parent == NIL)
14         T->root = y;                //若是x的父結點爲哨兵,說明x爲根結點,則y成爲根結點
15     else if(x == x->parent->left)
16         x->parent->left = y;        
17     else
18         x->parent->right = y;       //判斷x爲其父結點的左、右兒子,y成爲x父結點對應的兒子
19     y->left = x;                    //y的左兒子爲x
20     x->parent = y;                  //x的父結點爲y
21 }
22 
23 /*
24  * x結點上進行右旋轉,y結點(x結點的左兒子)的右兒子成爲x結點的新左兒子
25  * x結點成爲y結點的右兒子的新父結點
26  * x結點的父結點成爲y結點的新父結點,y結點成爲x結點的新父結點
27  * x結點成爲y結點的新右兒子
28  * PS:代碼的解釋可參照左旋轉
29  */
30 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){
31     RB_TreeNode *y = x->left;
32     x->left = y->right;
33     if(y->right != NIL)
34         y->right->parent = x;
35     y->parent = x->parent;
36     if(x->parent == NIL)
37         T->root = y;
38     else if(x == x->parent->left)
39         x->parent->left = y;
40     else
41         x->parent->right = y;
42     y->right = x;
43     x->parent = y;
44 }
View Code

選擇效果例子以下圖:

有了旋轉操做,咱們就能夠進行插入和刪除操做。紅黑樹的插入和刪除,是基於普通二叉查找樹的插入和刪除,再添加維持紅黑性質的操做(PS:二叉查找樹的插入、刪除操做見以前的博客)。

插入

當新的結點插入到紅黑樹時,咱們將該結點的顏色設置爲紅色,這樣紅黑樹的性質1(沒有結點存在其餘顏色)、3(新結點的左右兒子爲NIL,黑色結點)、5(添加紅色結點不影響黑色結點的數目)沒改變,可是有性質2(新結點有可能爲根結點)和性質4(新結點的父結點有可能也是紅色的)。PS:該段中括號爲對應的解釋。。。。

插入新結點後,若是它不是根結點並且它的父結點不是紅色的,那麼就無需作有關調整。可是若是不符合紅黑性質的時候就須要作有關調整,若是其爲根結點,直接將其顏色設置爲黑色便可。除此以外,還存在3種狀況不符合紅黑性質(PS:當前的狀況可能通過一次調整後成爲其餘狀況。具體來說應該有6種狀況,不過有3種和另外3種對稱,下文是考慮z結點的父結點爲左兒子):

狀況1:結點z的叔叔是紅色。這樣咱們直接將z父親的父親顏色變爲紅色,z的父親和叔叔的顏色變爲黑色(這樣性質5沒有改變),最後將z父親的父親做爲新的z結點,繼續上移,直到z結點的父結點爲黑色或者z爲根結點(當z爲根結點,直接將其顏色改成黑色,這樣整個紅黑樹就比原來多了一個黑色結點。可是改變顏色前已經通過各類調整,只違反性質2,改變顏色後,就沒有違反任何紅黑性質)。以下圖所示:

狀況2:結點z的叔叔是黑色的、並且z是右兒子(狀況2能夠經過旋轉成爲狀況3)

狀況3:結點z的叔叔是黑色的、並且z是左兒子(PS:若是z結點的父結點爲右兒子的時候狀況2要改成「z是左兒子」,狀況3要改成「z是右兒子」)由於z的叔叔是黑色,因此不能夠直接經過改變顏色來維持紅黑性質,要經過旋轉操做來維持。以下圖所示,若是爲狀況2就經過對A進行左旋轉變爲狀況3,再改變結點的顏色,最後將C進行右旋轉。這樣就使得解決違反性質4,並且不違反性質5.(PS:若是z結點的父結點爲右兒子的時候,應將左旋轉改成右旋轉,右旋轉改成左旋轉。具體畫圖理一理就明白

下爲插入和插入調整實現(PS:插入的註釋見二叉查找樹的插入)

 1 /*
 2  * 插入函數,注意插入結點與其在樹中對應的父結點的連接(須要記錄父結點)。
 3  * 從根結點出發,不停用當前結點與插入的值比較,若是當前結點的值比較大就往當前結點的左兒子走,相反就往右兒子走,直到當前結點爲空,
 4  * 在過程當中記錄當前結點的父結點。
 5  * 運行時間爲O(h),h爲樹的高度。由於整個過程是一條沿着根結點降低的路徑。
 6  */
 7 void RB_Insert(RB_Tree *T, int key){
 8     RB_TreeNode *z;
 9     z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode));         //新建結點
10     z->value = key;
11     z->color = RED;
12     z->parent = z->left = z->right = NIL;
13     RB_TreeNode *x = T->root;
14     RB_TreeNode *y = NIL;
15     while(x != NIL){
16         y = x;
17         if(x->value < key)
18             x = x->right;
19         else
20             x = x->left;
21     }
22     z->parent = y;
23     if(y == NIL){
24         T->root = z;
25         T->root->parent = NIL;
26         T->root->parent->color = BLACK;
27     }
28     else if(z->value < y->value)
29         y->left = z;
30     else
31         y->right = z;
32     RB_Insert_Fixup(T, z);                                 //插入調整,維持紅黑性質
33 }
34 
35 /*
36  * 插入調整,維持紅黑性質
37  */
38 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){
39     while(z->parent->color == RED){                         //若是z結點的父結點爲黑色,就不用進入3種狀況的處理
40         if(z->parent == z->parent->parent ->left){          //z的父結點爲左兒子的狀況
41             RB_TreeNode *y = z->parent->parent->right;
42             if(y->color == RED){                            //狀況1
43                 z->parent->color = BLACK;
44                 y->color = BLACK;
45                 z->parent->parent->color = RED;
46                 z = z->parent->parent;
47             }                                           
48             else {
49                 if(z == z->parent->right){                  //狀況2
50                     z = z->parent;
51                     Left_Rotate(T, z);                      //左旋轉,狀況2變爲3
52                 }
53                 z->parent->color = BLACK;                   //狀況3
54                 z->parent->parent->color = RED;
55                 Right_Rotate(T, z->parent->parent);         //右選擇解決違反性質4
56             }
57         }
58         else{
59             RB_TreeNode *y = z->parent->parent->left;       //z的結點爲右兒子的狀況
60             if(y->color == RED){                            //狀況1
61                 z->parent->color = BLACK;
62                 y->color = BLACK;
63                 z->parent->parent->color = RED;
64                 z = z->parent->parent;
65             }
66             else{
67                 if(z == z->parent->left){                   //狀況2
68                     z = z->parent;
69                     Right_Rotate(T, z);                     //右旋轉,狀況2變爲狀況3
70                 }
71                 z->parent->color = BLACK;                   //狀況3
72                 z->parent->parent->color = RED;
73                 Left_Rotate(T, z->parent->parent);          //左旋轉解決違反性質4
74             }
75         }
76     }
77     T->root->color = BLACK;                                 //若是紅黑樹T的根結點爲紅色就會變成黑色,若是是黑色變成黑色也沒影響
78 }
View Code

紅黑樹的插入操做總共花費時間爲O(lgn)。由於插入部分花費O(lgn),而插入調整隻有狀況1執行的時候纔可能要重複進行while循環,所以while循環可能的運行次數爲O(lgn)。因此總共花費爲O(lgn)。

刪除(PS:下文的z爲想刪除的結點、y結點爲實際要刪除的結點、x結點爲y的惟一孩子結點。若是以爲想刪除結點、實際刪除結點、實際刪除結點的惟一孩子感到困惑,請看二叉查找樹,裏面有解釋,雖然裏面的命名又點不同。。。)

對於刪除結點,若是刪除的是紅色的結點,對紅黑性質沒有任何影響。可是若是刪除的是黑色結點,就會產生3個問題:一、若是y是根結點,而y的一個紅色孩子成爲了根結點,違反了性質2;二、若是x和y的父結點都是紅色,違反性質4;三、刪除y致使包含y的任何路徑黑結點個數少1,違反了性質5,解決這個問題的辦法是x添加多一層黑色(雙重黑色或紅黑),這樣就由違反性質5變爲違反性質1。(PS:若是要恢復性質5會很麻煩。。。。這個我是看網友博客了。。還沒探究這個問題)

對於刪除調整如何恢復性質二、4:刪除調整實現裏對於x爲根結點或者x的顏色爲紅色時直接將x結點的顏色變成黑色。若是x爲根結點,直接改變爲黑色便可,若是x不爲根結點,將x變成黑色,能夠解決違反性質4,並且以前包含y的任何路徑的黑色結點個數也恢復。(這個是我本身看完僞代碼想的,書中要求讀者說明刪除調整代碼如何恢復二、4,因此可能有錯,歡迎指出錯誤)。

刪除調整主要將額外的黑色沿樹上移,直到:一、x指向一個紅黑結點,直接將x的顏色變爲黑色

                    二、x指向根,直接消除額外的黑色

                    三、或者作必要的旋轉和顏色修改

總共8狀況(4種與另外4種對稱,下文是考慮x爲左兒子的狀況)(下文的x結點爲須要調整的結點,即增長了額外一層黑色)

狀況1:x的兄弟w是紅色。先改變w和x的父結點的顏色,對x的父結點進行一次作旋轉,紅黑性質繼續維持,可是咱們經過這些操做使得狀況1轉換成狀況二、三、4

狀況2:x的兄弟w是黑色,並且w的兩個孩子都是黑色。x和w的去掉一重黑色,那麼x只有一重黑色,而w變爲紅色(由於w的兩個兒子都是黑色因此變成紅色沒有違反性質4)。而後咱們再在x的父結點上添加一層額外的黑色,最後x的父結點成爲新的x結點繼續我和循環。

狀況3:x的兄弟w是黑色的,w的左孩子是紅色,右孩子是黑色的。首先交換w和其左孩子的顏色,對w進行右旋轉。這樣就從狀況3轉換成狀況4.

狀況4:x的兄弟w是黑色的,並且w的右孩子是紅色的。首先交換w和x的父結點的顏色,對x的父結點進行左旋轉,去掉x的額外黑色,又不破壞紅黑性質。最後將x置爲根結點,接受循環。

(PS:x爲右兒子的狀況時,須要將上文的左旋轉換成右旋轉,右旋轉換成左旋轉)

具體以下圖所示

下爲刪除和刪除調整實現:

 1 /*
 2  * 刪除結點函數,首先要肯定真正刪除的結點是那個。
 3  * 若是z沒有子結點,直接將z的父結點對應的指針指向NULL
 4  * 若是z只有一個子節點,直接將z的父結點對應的指針指向z的子結點
 5  * 若是z有兩個子結點,實際上要刪除的不是z,而是z的後繼,,再用z的後繼的內容代替z的內容
 6  */
 7 
 8 void RB_Delete(RB_Tree *T, RB_TreeNode *z){
 9     RB_TreeNode *x = NULL;
10     RB_TreeNode *y = NULL;
11 
12     if(z->left == NIL || z->right == NIL)
13         y = z;
14     else
15         y = RB_Tree_Successor(z);
16     if(y->left != NIL)
17         x = y->left;
18     else
19         x = y->right;
20     x->parent = y->parent;
21     if(y->parent == NIL)
22         T->root = x;
23     else if(y == y->parent->left)
24         y->parent->left = x;
25     else
26         y->parent->right = x;
27     if(y != z)
28         z->value = y->value;
29     if(y->color == BLACK)
30         RB_Delete_Fixup(T, x);          //當實際要刪除的結點y爲黑色時才須要進行調整
31 }
32 
33 /*
34  * 刪除調整,使紅黑樹維持紅黑性質
35  */
36 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){
37     while(x != T->root && x->color == BLACK){               //當x結點爲根結點或者x的顏色爲紅色(即爲紅黑, 實際上額外黑色沒有直接加上去,只是默認x節點有一重額外的黑色)
38         if(x == x->parent->left){                           //x爲左兒子狀況
39             RB_TreeNode *w = x->parent->right;
40             if(w->color == RED){                            //狀況1
41                 w->color = BLACK;
42                 x->parent->color = RED;
43                 Left_Rotate(T, x->parent);                  //左旋轉,使得狀況1轉換成狀況二、3或4
44                 w = x->parent->right;                       
45             }
46             
47             if(w->left->color == BLACK && w->right->color == BLACK){      //狀況2
48                  w->color = RED;                            //修改顏色
49                  x = x->parent;                             //上移
50             }
51             else{
52                 if(w->right->color == BLACK){               //狀況3
53                     w->left->color = BLACK;
54                     w->color = RED;
55                     Right_Rotate(T, w);                     //右旋轉,狀況3轉換成狀況4
56                     w = x->parent->right;
57                 }
58                 w->color = x->parent->color;
59                 x->parent->color = BLACK;
60                 w->right->color = BLACK;
61                 Left_Rotate(T, x->parent);                  //左旋轉,去掉x的額外黑色
62                 x = T->root;                                //使循環結束
63             }
64             
65         }
66         else{                                               //x爲右兒子狀況(PS:下文的註釋參考上文)
67             RB_TreeNode *w = x->parent->left;
68             if(w->color == RED){
69                 w->color = BLACK;
70                 x->parent->color = RED;
71                 Right_Rotate(T, x->parent);
72                 w = x->parent->left;
73             }
74             
75             if(w->left->color == BLACK && w->right->color == BLACK){
76                 w->color = RED;
77                 x = x->parent;
78             }
79             else {
80                 if(w->left->color == BLACK){
81                     w->right->color = BLACK;
82                     w->color = RED;
83                     Left_Rotate(T, w);
84                     w = x->parent->left;
85                 }
86                 w->color = x->parent->color;
87                 x->parent->color = BLACK;
88                 w->left->color = BLACK;
89                 Right_Rotate(T, x->parent);
90                 x = T->root;
91             }
92         }
93     }
94 }
View Code

刪除操做的總運行時間爲O(lgn)。

 

下爲所有代碼(除了刪除、插入實現的註釋見二叉查找樹

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define RED 1
  5 #define BLACK 0
  6 
  7 typedef struct RB_TreeNode {
  8     int value;
  9     int color;
 10     struct RB_TreeNode * parent;
 11     struct RB_TreeNode * left;
 12     struct RB_TreeNode * right;
 13 }RB_TreeNode;                       //紅黑樹結點結構體
 14 
 15 typedef struct RB_Tree {
 16     RB_TreeNode * root;
 17 }RB_Tree;                           //紅黑樹結構體,根結點
 18 
 19 RB_TreeNode  NILL = { -1, BLACK, NULL, NULL, NULL };
 20 RB_TreeNode *NIL = &NILL;                               //NIL結點指針
 21 
 22 void Left_Rotate(RB_Tree *T, RB_TreeNode *x);
 23 
 24 void Right_Rotate(RB_Tree *T, RB_TreeNode *x);
 25 
 26 void RB_Insert(RB_Tree *T, int key);
 27 
 28 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z);
 29 
 30 void RB_Delete(RB_Tree *T, RB_TreeNode *z);
 31 
 32 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x);
 33 
 34 RB_TreeNode * RB_Search(RB_TreeNode *x, int key);
 35 
 36 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x);
 37 
 38 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x);
 39 
 40 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x);
 41 
 42 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x);
 43 
 44 void Inorder_RB_Tree_Walk(RB_TreeNode *x);
 45 
 46 void free_men(RB_TreeNode *x);
 47 
 48 int main(){
 49     RB_Tree *T;
 50     T->root = NIL;
 51 
 52     int n, value, i;
 53     scanf("%d", &n);
 54     for(i = 1; i<= n; i++){
 55         scanf("%d", &value);
 56         RB_Insert(T, value);
 57         Inorder_RB_Tree_Walk(T->root);    
 58         printf("\n");
 59     }
 60     RB_TreeNode *s = RB_Search(T->root, 3);
 61     if(s != NIL)
 62         printf("%d is exists!\n", s->value);
 63     /*Inorder_RB_Tree_Walk(T->root);
 64 
 65     printf("\n");*/
 66     printf("%d\n", RB_Tree_Minimum(T->root)->value);
 67     printf("%d\n", RB_Tree_Maximum(T->root)->value);
 68     printf("%d\n", RB_Tree_Successor(s)->value);
 69     printf("%d\n", RB_Tree_Predecesor(s)->value);
 70     RB_Delete(T, s);
 71     Inorder_RB_Tree_Walk(T->root);
 72     printf("\n");
 73     return 0;
 74 }
 75 
 76 /*
 77  * x結點上進行左旋轉,y結點(x結點的右兒子)的左兒子成爲x結點的新右兒子
 78  * x結點成爲y結點的左兒子的新父結點
 79  * x結點的父結點成爲y結點的新父結點,y結點成爲x結點的新父結點
 80  * x結點成爲y結點的新左兒子
 81  */
 82 void Left_Rotate(RB_Tree *T, RB_TreeNode *x){
 83     RB_TreeNode *y = x->right;       //x點右兒子          
 84     x->right = y->left;              //y結點的左兒子會成爲x結點的右兒子
 85     if(y->left != NIL)
 86         y->left->parent = x;         //若是y有左兒子,y的左兒子的父結點爲x
 87     y->parent = x->parent;
 88     if(x->parent == NIL)
 89         T->root = y;                //若是x的父結點爲哨兵,說明x爲根結點,則y成爲根結點
 90     else if(x == x->parent->left)
 91         x->parent->left = y;        
 92     else
 93         x->parent->right = y;       //判斷x爲其父結點的左、右兒子,y成爲x父結點對應的兒子
 94     y->left = x;                    //y的左兒子爲x
 95     x->parent = y;                  //x的父結點爲y
 96 }
 97 
 98 /*
 99  * x結點上進行右旋轉,y結點(x結點的左兒子)的右兒子成爲x結點的新左兒子
100  * x結點成爲y結點的右兒子的新父結點
101  * x結點的父結點成爲y結點的新父結點,y結點成爲x結點的新父結點
102  * x結點成爲y結點的新右兒子
103  * PS:代碼的解釋可參照左旋轉
104  */
105 void Right_Rotate(RB_Tree *T, RB_TreeNode *x){
106     RB_TreeNode *y = x->left;
107     x->left = y->right;
108     if(y->right != NIL)
109         y->right->parent = x;
110     y->parent = x->parent;
111     if(x->parent == NIL)
112         T->root = y;
113     else if(x == x->parent->left)
114         x->parent->left = y;
115     else
116         x->parent->right = y;
117     y->right = x;
118     x->parent = y;
119 }
120 
121 /*
122  * 插入函數,注意插入結點與其在樹中對應的父結點的連接(須要記錄父結點)。
123  * 從根結點出發,不停用當前結點與插入的值比較,若是當前結點的值比較大就往當前結點的左兒子走,相反就往右兒子走,直到當前結點爲空,
124  * 在過程當中記錄當前結點的父結點。
125  * 運行時間爲O(h),h爲樹的高度。由於整個過程是一條沿着根結點降低的路徑。
126  */
127 void RB_Insert(RB_Tree *T, int key){
128     RB_TreeNode *z;
129     z = (RB_TreeNode *)malloc(sizeof(RB_TreeNode));         //新建結點
130     z->value = key;
131     z->color = RED;
132     z->parent = z->left = z->right = NIL;
133     RB_TreeNode *x = T->root;
134     RB_TreeNode *y = NIL;
135     while(x != NIL){
136         y = x;
137         if(x->value < key)
138             x = x->right;
139         else
140             x = x->left;
141     }
142     z->parent = y;
143     if(y == NIL){
144         T->root = z;
145         T->root->parent = NIL;
146         T->root->parent->color = BLACK;
147     }
148     else if(z->value < y->value)
149         y->left = z;
150     else
151         y->right = z;
152     RB_Insert_Fixup(T, z);                                 //插入調整,維持紅黑性質
153 }
154 
155 /*
156  * 插入調整,維持紅黑性質
157  */
158 void RB_Insert_Fixup(RB_Tree *T, RB_TreeNode *z){
159     while(z->parent->color == RED){                         //若是z結點的父結點爲黑色,就不用進入3種狀況的處理
160         if(z->parent == z->parent->parent ->left){          //z的父結點爲左兒子的狀況
161             RB_TreeNode *y = z->parent->parent->right;
162             if(y->color == RED){                            //狀況1
163                 z->parent->color = BLACK;
164                 y->color = BLACK;
165                 z->parent->parent->color = RED;
166                 z = z->parent->parent;
167             }                                           
168             else {
169                 if(z == z->parent->right){                  //狀況2
170                     z = z->parent;
171                     Left_Rotate(T, z);                      //左旋轉,狀況2變爲3
172                 }
173                 z->parent->color = BLACK;                   //狀況3
174                 z->parent->parent->color = RED;
175                 Right_Rotate(T, z->parent->parent);         //右選擇解決違反性質4
176             }
177         }
178         else{
179             RB_TreeNode *y = z->parent->parent->left;       //z的結點爲右兒子的狀況
180             if(y->color == RED){                            //狀況1
181                 z->parent->color = BLACK;
182                 y->color = BLACK;
183                 z->parent->parent->color = RED;
184                 z = z->parent->parent;
185             }
186             else{
187                 if(z == z->parent->left){                   //狀況2
188                     z = z->parent;
189                     Right_Rotate(T, z);                     //右旋轉,狀況2變爲狀況3
190                 }
191                 z->parent->color = BLACK;                   //狀況3
192                 z->parent->parent->color = RED;
193                 Left_Rotate(T, z->parent->parent);          //左旋轉解決違反性質4
194             }
195         }
196     }
197     T->root->color = BLACK;                                 //若是紅黑樹T的根結點爲紅色就會變成黑色,若是是黑色變成黑色也沒影響
198 }
199 
200 /*
201  * 刪除結點函數,首先要肯定真正刪除的結點是那個。
202  * 若是z沒有子結點,直接將z的父結點對應的指針指向NULL
203  * 若是z只有一個子節點,直接將z的父結點對應的指針指向z的子結點
204  * 若是z有兩個子結點,實際上要刪除的不是z,而是z的後繼,,再用z的後繼的內容代替z的內容
205  */
206 
207 void RB_Delete(RB_Tree *T, RB_TreeNode *z){
208     RB_TreeNode *x = NULL;
209     RB_TreeNode *y = NULL;
210 
211     if(z->left == NIL || z->right == NIL)
212         y = z;
213     else
214         y = RB_Tree_Successor(z);
215     if(y->left != NIL)
216         x = y->left;
217     else
218         x = y->right;
219     x->parent = y->parent;
220     if(y->parent == NIL)
221         T->root = x;
222     else if(y == y->parent->left)
223         y->parent->left = x;
224     else
225         y->parent->right = x;
226     if(y != z)
227         z->value = y->value;
228     if(y->color == BLACK)
229         RB_Delete_Fixup(T, x);          //當實際要刪除的結點y爲黑色時才須要進行調整
230 }
231 
232 /*
233  * 刪除調整,使紅黑樹維持紅黑性質
234  */
235 void RB_Delete_Fixup(RB_Tree *T, RB_TreeNode *x){
236     while(x != T->root && x->color == BLACK){               //當x結點爲根結點或者x的顏色爲紅色(即爲紅黑, 實際上額外黑色沒有直接加上去,只是默認x節點有一重額外的黑色)
237         if(x == x->parent->left){                           //x爲左兒子狀況
238             RB_TreeNode *w = x->parent->right;
239             if(w->color == RED){                            //狀況1
240                 w->color = BLACK;
241                 x->parent->color = RED;
242                 Left_Rotate(T, x->parent);                  //左旋轉,使得狀況1轉換成狀況二、3或4
243                 w = x->parent->right;                       
244             }
245             
246             if(w->left->color == BLACK && w->right->color == BLACK){      //狀況2
247                  w->color = RED;                            //修改顏色
248                  x = x->parent;                             //上移
249             }
250             else{
251                 if(w->right->color == BLACK){               //狀況3
252                     w->left->color = BLACK;
253                     w->color = RED;
254                     Right_Rotate(T, w);                     //右旋轉,狀況3轉換成狀況4
255                     w = x->parent->right;
256                 }
257                 w->color = x->parent->color;
258                 x->parent->color = BLACK;
259                 w->right->color = BLACK;
260                 Left_Rotate(T, x->parent);                  //左旋轉,去掉x的額外黑色
261                 x = T->root;                                //使循環結束
262             }
263             
264         }
265         else{                                               //x爲右兒子狀況(PS:下文的註釋參考上文)
266             RB_TreeNode *w = x->parent->left;
267             if(w->color == RED){
268                 w->color = BLACK;
269                 x->parent->color = RED;
270                 Right_Rotate(T, x->parent);
271                 w = x->parent->left;
272             }
273             
274             if(w->left->color == BLACK && w->right->color == BLACK){
275                 w->color = RED;
276                 x = x->parent;
277             }
278             else {
279                 if(w->left->color == BLACK){
280                     w->right->color = BLACK;
281                     w->color = RED;
282                     Left_Rotate(T, w);
283                     w = x->parent->left;
284                 }
285                 w->color = x->parent->color;
286                 x->parent->color = BLACK;
287                 w->left->color = BLACK;
288                 Right_Rotate(T, x->parent);
289                 x = T->root;
290             }
291         }
292     }
293 }
294 
295 
296 RB_TreeNode * RB_Search(RB_TreeNode *x, int key){
297     if(x->value == key || x == NIL)
298         return x;
299     if(x->value > key)
300         RB_Search(x->left, key);
301     else
302         RB_Search(x->right, key);
303 }
304 
305 RB_TreeNode * RB_Tree_Minimum(RB_TreeNode *x){
306     RB_TreeNode *r = x;
307     while(r->left != NIL)
308         r = r->left;
309     return r;
310 }
311 
312 RB_TreeNode * RB_Tree_Maximum(RB_TreeNode *x){
313     RB_TreeNode *r = x;
314     while(r->left != NIL)
315         r = r->left;
316     return r;
317 }
318 
319 RB_TreeNode * RB_Tree_Successor(RB_TreeNode *x){
320     RB_TreeNode *r = x;
321     if(r->right != NIL)
322         return RB_Tree_Minimum(r->right);
323     RB_TreeNode *y = r->parent;
324     while(y != NIL && r == y->right){
325         r = y;
326         y = y->parent;
327     }
328     return y;
329 }
330 RB_TreeNode * RB_Tree_Predecesor(RB_TreeNode *x){
331     RB_TreeNode *r = x;
332     if(r->left != NIL)
333         return RB_Tree_Maximum(r->left);
334     RB_TreeNode *y = r->parent;
335     while(y != NIL && r == y->left){
336         r = y;
337         y = y->parent;
338     }
339     return y;
340 }
341 
342 void Inorder_RB_Tree_Walk(RB_TreeNode *x){
343     if(x != NIL){
344         Inorder_RB_Tree_Walk(x->left);
345         printf("%d : ", x->value);
346         if(x->color == 1)
347             printf("red ");
348         else
349             printf("black ");
350         Inorder_RB_Tree_Walk(x->right);
351     }
352 }
353 
354 void free_men(RB_TreeNode *x){
355     if(x != NIL){
356         free_men(x->left);
357         free_men(x->right);
358         free(x);
359     }
360 }
View Code

 

PS:吐槽來了。。。之後要多更新博客,打算把當天學到有感受的東西記錄下來。。。算導可能更新會有點慢(已經兩個星期才一篇。。。太弱菜了,越到最後要好好磨)。。。打算寫SICP學習記錄和Linux學校記錄。。。。。好好努力吧!!!博客能更新真開心。。。。。

相關文章
相關標籤/搜索