一、概念1.定義與性質(1)定義紅/黑樹狀結構字義:滿足(a)每個結點或是紅的,或是黑的(b)根結點是黑的(c)每個葉結點(NIL)是黑的(d)如果一個結點是紅的,則它的兩個兒子是黑的(e)對每個結點,從該結點到其子孫結點的所有路徑上包含相同數目的黑結點的二叉尋找樹稱為紅/黑樹狀結構。黑高度定義:從某個結點x出發(不包括該結點)到達一個葉結點的任意一條路徑上的黑色結點的個數稱為x的黑高度。(2)性質紅/黑樹狀結構確保沒有一條路徑會比其它路徑長出兩倍。一棵有n個內結點的紅/黑樹狀結構的高度至多為2lg(n+1)2.結構(1)紅/黑樹狀結構結點結構
struct node{node *left;node *right;node *p;int key;bool color;};
(2)紅/黑樹狀結構結構
struct Red_Black_Tree{node *root;//根結點node *nil;//哨兵};
3.紅/黑樹狀結構上的操作SEARCHPREDECESSORMINIMUMMAXIMUMINSERTDELETE二、代碼(1)Red_Black_Tree.h
#include <iostream>using namespace std;#define BLACK 0#define RED 1//紅/黑樹狀結構結點結構struct node{node *left;node *right;node *p;int key;bool color;node(node *init, int k):left(init),right(init),p(init),key(k),color(BLACK){}};//紅/黑樹狀結構結構class Red_Black_Tree{public:node *root;//根結點node *nil;//哨兵Red_Black_Tree(){nil = new node(NULL, -1);root = nil;};//13.2旋轉void Left_Rotate(node *x);void Right_Rotate(node *x);//13.3插入void RB_Insert_Fixup(node *z);void RB_Insert(node *z);//13.4刪除void RB_Delete_Fixup(node *x);node *RB_Delete(node *z);//elsevoid Print();void Print(node *x);node *RB_Search(node *x, int k);node *Tree_Successor(node *x);node *Tree_Minimum(node *x);};//左旋,令y = x->right, 左旋是以x和y之間的鏈為支軸進行旋轉//涉及到的結點包括:x,y,y->left,令node={p,l,r},具體變化如下://x={x->p,x->left,y}變為{y,x->left,y->left}//y={x,y->left,y->right}變為{x->p,x,y->right}//y->left={y,y->left->left,y->left->right}變為{x,y->left->left,y->left->right}void Red_Black_Tree::Left_Rotate(node *x){//令y = x->rightnode *y = x->right;//按照上面的方式修改三個結點的指標,注意修改指標的順序x->right = y->left;if(y->left != nil)y->left->p = x;y->p = x->p;if(x->p == nil)//特殊情況:x是根結點root = y;else if(x == x->p->left)x->p->left = y;else x->p->right = y;y->left = x;x->p = y;}//右旋,令y = x->left, 左旋是以x和y之間的鏈為支軸進行旋轉//旋轉過程與上文類似void Red_Black_Tree::Right_Rotate(node *x){node *y = x->left;x->left = y->right;if(y->right != nil)y->right->p = x;y->p = x->p;if(x->p == nil)root = y;else if(x == x->p->right)x->p->right = y;else x->p->left = y;y->right = x;x->p = y;}//紅/黑樹狀結構調整void Red_Black_Tree::RB_Insert_Fixup(node *z){node *y;//唯一需要調整的情況,就是違反性質2的時候,如果不違反性質2,調整結束while(z->p->color == RED){//p[z]是左孩子時,有三種情況if(z->p == z->p->p->left){//令y是z的叔結點y = z->p->p->right;//第一種情況,z的叔叔y是紅色的if(y->color == RED){//將p[z]和y都著為黑色以解決z和p[z]都是紅色的問題z->p->color = BLACK;y->color = BLACK;//將p[p[z]]著為紅色以保持性質5z->p->p->color = RED;//把p[p[z]]當作新增的結點z來重複while迴圈z = z->p->p;}else{//第二種情況:z的叔叔是黑色的,且z是右孩子if(z == z->p->right){//對p[z]左旋,轉為第三種情況z = z->p;Left_Rotate(z);}//第三種情況:z的叔叔是黑色的,且z是左孩子//交換p[z]和p[p[z]]的顏色,並右旋z->p->color = BLACK;z->p->p->color = RED;Right_Rotate(z->p->p);}}//p[z]是右孩子時,有三種情況,與上面類似else if(z->p == z->p->p->right){y = z->p->p->left;if(y->color == RED){z->p->color = BLACK;y->color = BLACK;z->p->p->color = RED;z = z->p->p;}else{if(z == z->p->left){z = z->p;Right_Rotate(z);}z->p->color = BLACK;z->p->p->color = RED;Left_Rotate(z->p->p);}}}//根結點置為黑色root->color = BLACK;}//插入一個結點void Red_Black_Tree::RB_Insert(node *z){node *y = nil, *x = root;//找到應該插入的位置,與二叉尋找樹的插入相同while(x != nil){y = x;if(z->key < x->key)x = x->left;elsex = x->right;}z->p = y;if(y == nil)root = z;else if(z->key < y->key)y->left = z;elsey->right = z;z->left = nil;z->right = nil;//將新插入的結點轉為紅色z->color = RED;//從新插入的結點開始,向上調整RB_Insert_Fixup(z);}//對樹進行調整,x指向一個紅黑結點,調整的過程是將額外的黑色沿樹上移void Red_Black_Tree::RB_Delete_Fixup(node *x){node *w;//如果這個額外的黑色在一個根結點或一個紅結點上,結點會吸收額外的黑色,成為一個黑色的結點while(x != root && x->color == BLACK){//若x是其父的左結點(右結點的情況相對應)if(x == x->p->left){//令w為x的兄弟,根據w的不同,分為三種情況來處理//執行刪除操作前x肯定是沒有兄弟的,執行刪除操作後x肯定是有兄弟的w = x->p->right;//第一種情況:w是紅色的if(w->color == RED){//改變w和p[x]的顏色w->color = BLACK;x->p->color = RED;//對p[x]進行一次左旋Left_Rotate(x->p);//令w為x的新兄弟w = x->p->right;//轉為2.3.4三種情況之一}//第二情況:w為黑色,w的兩個孩子也都是黑色if(w->left->color == BLACK && w->right->color == BLACK){//去掉w和x的黑色//w只有一層黑色,去掉變為紅色,x有多餘的一層黑色,去掉後恢複原來顏色w->color = RED;//在p[x]上補一層黑色x = x->p;//現在新x上有個額外的黑色,轉入for迴圈繼續處理}//第三種情況,w是黑色的,w->left是紅色的,w->right是黑色的else{if(w->right->color == BLACK){//改變w和left[x]的顏色w->left->color = BLACK;w->color = RED;//對w進行一次右旋Right_Rotate(w);//令w為x的新兄弟w = x->p->right;//此時轉變為第四種情況}//第四種情況:w是黑色的,w->left是黑色的,w->right是紅色的//修改w和p[x]的顏色w->color =x->p->color;x->p->color = BLACK;w->right->color = BLACK;//對p[x]進行一次左旋Left_Rotate(x->p);//此時調整已經結束,將x置為根結點是為了結束迴圈x = root;}}//若x是其父的左結點(右結點的情況相對應)else if(x == x->p->right){//令w為x的兄弟,根據w的不同,分為三種情況來處理//執行刪除操作前x肯定是沒有兄弟的,執行刪除操作後x肯定是有兄弟的w = x->p->left;//第一種情況:w是紅色的if(w->color == RED){//改變w和p[x]的顏色w->color = BLACK;x->p->color = RED;//對p[x]進行一次左旋Right_Rotate(x->p);//令w為x的新兄弟w = x->p->left;//轉為2.3.4三種情況之一}//第二情況:w為黑色,w的兩個孩子也都是黑色if(w->right->color == BLACK && w->left->color == BLACK){//去掉w和x的黑色//w只有一層黑色,去掉變為紅色,x有多餘的一層黑色,去掉後恢複原來顏色w->color = RED;//在p[x]上補一層黑色x = x->p;//現在新x上有個額外的黑色,轉入for迴圈繼續處理}//第三種情況,w是黑色的,w->right是紅色的,w->left是黑色的else{if(w->left->color == BLACK){//改變w和right[x]的顏色w->right->color = BLACK;w->color = RED;//對w進行一次右旋Left_Rotate(w);//令w為x的新兄弟w = x->p->left;//此時轉變為第四種情況}//第四種情況:w是黑色的,w->right是黑色的,w->left是紅色的//修改w和p[x]的顏色w->color =x->p->color;x->p->color = BLACK;w->left->color = BLACK;//對p[x]進行一次左旋Right_Rotate(x->p);//此時調整已經結束,將x置為根結點是為了結束迴圈x = root;}}}//吸收了額外的黑色x->color = BLACK;}//找最小值 node *Red_Black_Tree::Tree_Minimum(node *x) { //只要有比當前結點小的結點 while(x->left != nil) x = x->left; return x; } //尋找中序遍曆下x結點的後繼,後繼是大於key[x]的最小的結點 node *Red_Black_Tree::Tree_Successor(node *x) { //如果有右孩子 if(x->right != nil) //右子樹中的最小值 return Tree_Minimum(x->right); //如果x的右子樹為空白且x有後繼y,那麼y是x的最低祖先結點,且y的左兒子也是 node *y = x->p; while(y != NULL && x == y->right) { x = y; y = y->p; } return y; } //遞迴地查詢二叉尋找樹 node *Red_Black_Tree::RB_Search(node *x, int k) { //找到葉子結點了還沒找到,或當前結點是所尋找的結點 if(x->key == -1 || k == x->key) return x; //所尋找的結點位於當前結點的左子樹 if(k < x->key) return RB_Search(x->left, k); //所尋找的結點位於當前結點的左子樹 else return RB_Search(x->right, k); } //紅/黑樹狀結構的刪除node *Red_Black_Tree::RB_Delete(node *z){//找到結點的位置並刪除,這一部分與二叉尋找樹的刪除相同node *x, *y;if(z->left == nil || z->right == nil)y = z;else y = Tree_Successor(z);if(y->left != nil)x = y->left;else x = y->right;x->p = y->p;if(y->p == nil)root = x;else if(y == y->p->left)y->p->left = x;elsey->p->right = x;if(y != z)z->key = y->key;//如果被刪除的結點是黑色的,則需要調整if(y->color == BLACK)RB_Delete_Fixup(x);return y;}void Red_Black_Tree::Print(node *x){if(x->key == -1)return;Print(x->left);cout<<x->key<<' '<<x->color<<endl;Print(x->right);}void Red_Black_Tree::Print(){Print(root);cout<<endl;}(2)main.cpp
#include <iostream>#include "Red_Black_Tree.h"using namespace std;int main(){Red_Black_Tree *T = new Red_Black_Tree();int s[6] = {41, 38, 31, 12, 19, 8}, i;for(i = 0; i< 6; i++){node *z = new node(T->nil, s[i]);T->RB_Insert(z);T->Print();}int s2[6] = {8, 12, 19, 31, 38, 41};for(i = 0; i< 6; i++){node *z = T->RB_Search(T->root, s2[i]);if(z != T->nil)T->RB_Delete(z);T->Print();}return 0;}三、練習13.1 紅/黑樹狀結構的性質
13.1-1
黑高度是指從根結點到葉結點的路徑上黑色結點的個數,需要注意的是,計算黑高度時,出發的結點不計算在內,葉結點是指nil結點
13.1-2
不是,因為違反性質4
不是,因為違反性質5
13.1-3
是
13.1-4
http://zh.clrs-ans.wikia.com/index.php?title=13.1_%E7%BA%A2%E9%BB%91%E6%A0%91%E7%9A%84%E6%80%A7%E8%B4%A8&variant=zh-cn
葉子深度為黑高度
13.1-5
bh(x)為黑高度,相應的定義rh(x)為紅高度,根據RB樹性質
rh(x)<=bh(x),而每條路徑的bh(x)都想等,最長可能路徑bh(x)+rh(x),最短可能路徑bh(x),故最長是最短的至多兩倍。
13.1-6
最多:2^(2k+1)-1
高少:2^(k)-1
13.1-7
比值最大為2:1,奇數層的結點為紅色,偶數層的結點為黑色。n為奇數。
比值最小為0,全部結點都為黑色
13.2 旋轉
13.2-1,代碼見上文RIGHT-ROTATE1 y <- left[x]2 left[x] <- right[y]3 if right[y] != nil[T]4 then p[right[y]] <- x5 p[y] <- p[x]6 if p[x] = nil[T]7 then root[T] <- y8 else if x = right[p[x]]9 then right[p[x]] <- y10 else left[p[x]] <- y11 right[y] <- x12 p[x] <- y13.2-3a的深度+1,b的深度不變,c的深度-1
13.3 插入
13.3-1
1樓說得很好
不是因為違反了性質5才不會採用把節點標誌位黑色。如果只是因為違反了性質5,完全可以調整,就跟RB-INSERT中將插入的節點標誌位紅色違反了性質4或者2一樣,都是可以通過跳整來符合要求。其原因是插入節點共有六種情況(通過RB-INSERT-FIXUP中的判斷語句可以看出:插入節點z的父節點為左孩子有三種,為右孩子也有三種。舉父節點為左孩子為例:第一種為如果z的叔叔為紅是第一種情況,如果叔叔為黑且z為右孩子為第二種情況,如果叔叔為黑且z為左孩子為第三種情況),前三種情況中只有第二種z最終為黑色節點,其餘兩種z最終為紅色。z最終為紅色的比例較高,(4:2),所以一開始將z定義為紅色,最後不用修改的機率較高,這是考慮到代碼的最佳化而不是因為違反了性質5。
13.3-2
運行以上程式能得到結果
13.3-6
見演算法導論-13.3-6
令待插入的元素是z。在插入的過程記錄從根結點到z的路徑,並用棧儲存。那麼z的父結點就是棧頂元素,z的祖父結點就是棧的次頂元素。
在向上迭代的過程同時出棧,控制好出棧的時間,就能正確實現RB-INSERT
13.4 刪除
13.4-3運行以上程式能得到結點13.4-4(1)某黑色結點被刪除後,第一個被調整的點是被刪除結點的孩子(這個被刪除的結點最多隻有一個孩子),若這個被刪除結點沒有孩子,那麼第一個被高速的點是nil(2)x的兄弟w沒有孩子,或左旋時w沒有左孩子,或右旋時w沒有右孩子13.4-7可能不相同。(1)新加入結點後可能會導致某些旋轉操作,刪除後不會轉回來(2)即使樹的結構不變,結點的顏色也有可能會改變
四、思考題13-1 動態持久集合
見演算法導論-13-1-持久動態集合
13-2 紅/黑樹狀結構上的串連操作
見演算法導論-13-2-紅/黑樹狀結構上的串連操作