《算法導論》學習記錄目錄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結點指針
一棵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 }
選擇效果例子以下圖:
有了旋轉操做,咱們就能夠進行插入和刪除操做。紅黑樹的插入和刪除,是基於普通二叉查找樹的插入和刪除,再添加維持紅黑性質的操做(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 }
紅黑樹的插入操做總共花費時間爲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 }
刪除操做的總運行時間爲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 }
PS:吐槽來了。。。之後要多更新博客,打算把當天學到有感受的東西記錄下來。。。算導可能更新會有點慢(已經兩個星期才一篇。。。太弱菜了,越到最後要好好磨)。。。打算寫SICP學習記錄和Linux學校記錄。。。。。好好努力吧!!!博客能更新真開心。。。。。