二叉搜索樹的結構:
typedef int ElemType; typedef struct SearchBiTree { ElemType Data; struct SearchBiTree *LChild,*RChild,*Parent; }SearchBiTree,*PSearchBiTree;
二叉搜索樹的性質:
設 x 是二叉搜索樹中的一個節點。如果 y 是 x 左子樹中的一個節點,那么 y.data <= x.data。
如果 y 是 x 右子樹中的一個節點,那么 y.data >= x.data。
不同的二叉搜索樹可以代表同一組值的集合。
插入代碼:
void Tree_Insert(PSearchBiTree &T,PSearchBiTree z) { PSearchBiTree y = NULL; PSearchBiTree x = T; while(x != NULL) { y = x; if(z->Data < x->Data) x = x->LChild; else x = x->RChild; } z->Parent = y; if(y == NULL) T = z; else if(z->Data < y->Data) y->LChild = z; else y->RChild = z; }
刪除操作:
刪除操作共有如下四種情況:
右下角的那種情況 Min 結點是 R子樹中值最小的一個結點,所以它的左孩子為空。
刪除代碼:
1、替換函數:將結點 v 替換 T 樹中的結點 u。
void Transplant(PSearchBiTree &T,PSearchBiTree u,PSearchBiTree v) { if(u->Parent == NULL) T = v; else if(u == u->Parent->LChild) u->Parent->LChild = v; else u->Parent->RChild = v; if(v != NULL) v->Parent = u->Parent; }
2、結點中的最小值。
PSearchBiTree Tree_Minimum(PSearchBiTree T) { while(T->LChild != NULL) T = T->LChild; return T; }
3、刪除結點 z。
void Tree_Delete(PSearchBiTree &T,PSearchBiTree z) { PSearchBiTree y = NULL; if(z->LChild == NULL) Transplant(T,z,z->RChild); else if(z->RChild == NULL) Transplant(T,z,z->LChild); else { y = Tree_Minimum(z->RChild); if(y->Parent == z) { Transplant(T,y,y->RChild); y->RChild = z->RChild; y->RChild->Parent = y; } Transplant(T,z,y); y->LChild = z->LChild; y->LChild->Parent = y;
}
}
紅黑樹:
算法導論中樹的高度似乎並不算樹根。
紅黑樹是許多"平衡"搜索樹中的一種,可以保證在最壞情況下基本動態集合操作的時間復雜度為O(lgn)。
紅黑樹是一顆二叉搜索樹,它相對二叉搜索樹增加了一個存儲位來標識結點顏色,可以使 Red 或 Black。
通過對任何一條從根到葉子的簡單路徑上各個結點的顏色進行約束,確保沒有一條路徑會比其他路徑長出兩倍。
我們通常把帶關鍵字的結點稱為內部結點,不帶關鍵字的結點並且其沒有子結點或父結點的結點稱為外部結點。
紅黑樹結構:
typedef enum {Red,Black}RB_Color; typedef struct RBTree { ElemType Data; struct RBTree *Left,*Right,*Parent; RB_Color Color; }RBTree,*PRBTree;
紅黑性質:
1、每個結點或是紅色的,或是黑色的。
2、根節點是黑色的。
3、每個葉結點是黑色的。
4、如果一個結點是紅色的,則它的兩個子結點都是黑色的。
5、對每一個結點,從該結點到其后代葉結點的簡單路徑上,均包含相同數目的黑色結點。
黑高bh:從某個結點 x 出發(不含該結點)到達一個葉結點的任意一條簡單路徑上的黑色結點個數,記作 bh(x)。
引理:一顆有 n 個內部結點的紅黑樹的高度至多為 2lg(n+1)。
推論:一顆高度為 h 的紅黑樹,黑高bh 至少為 (h/2)向上取整,最多為 h。
至少有 2^bh - 1 個結點,最多有 4^bh - 1個結點。
旋轉操作:
如圖,從右到左為左旋,從左到右為右旋。
旋轉代碼:
void Left_Rotate(PRBTree &T,PRBTree x) { PRBTree y = x->Right; x->Right = y->Left; if(y->Left != NULL) y->Left->Parent = x; y->Parent = x->Parent; if(x->Parent == NULL) T = y; else if(x == x->Parent->Left) x->Parent->Left = y; else x->Parent->Right = y; y->Left = x; x->Parent = y; } void Right_Rotate(PRBTree &T,PRBTree y) { PRBTree x = y->Left; y->Left = x->Right; if(x->Right != NULL) x->Right->Parent = y; x->Parent = y->Parent; if(y->Parent == NULL) T = x; else if(y == y->Parent->Left) y->Parent->Left = x; else y->Parent->Right = x; x->Right = y; y->Parent = x; }
插入操作:
在進行編寫代碼之前,需要分析一下所有的插入情況:
一、插入結點 A 的父結點 B 為黑色,此時插入不會破壞紅黑樹的性質。
二、插入結點 A 的父結點 B 為紅色,且 B 結點的兄弟也為紅色,
這時將不滿足性質 4。但可以作相應調整:
此時,將 B 結點以及 C 結點 變成黑色,將 D 結點變成紅色即可。
三、插入結點 A 的父結點 B 為紅色,但是 B 結點的兄弟為黑色,
這是也不滿足性質 4,也可以作出相應調整:
分別對以上四圖變化后,對圖一、圖二先變色后,分別右旋 DB,左旋 DB。
而圖三、圖四分別左旋 BA,右旋 BA 后,就變成了圖一、圖二。
相關操作如下所示:
插入代碼:
void RB_Insert(PRBTree &T,PRBTree z) { PRBTree y = NULL; PRBTree x = T; while(x != NULL) { y = x; if(z->Data < x->Data) x = x->Left; else x = x->Right; } z->Parent = y; if(y == NULL) T = z; else if(z->Data < y->Data) y->Left = z; else y->Right = z; z->Left = NULL; z->Right = NULL; z->Color = Red; RB_Insert_Fixup(T,z); }
插入修正代碼:
void RB_Insert_Fixup(PRBTree &T,PRBTree z) { PRBTree y = NULL; while(z->Parent->Color = Red) { if(z->Parent == z->Parent->Parent->Left) { y = z->Parent->Parent->Right; if(y->Color == Red) { z->Parent->Color = Black; y->Color = Black; z->Parent->Parent->Color = Red; z = z->Parent->Parent; } else if(z = z->Parent->Right) { z = z->Parent; Left_Rotate(T,z); } z->Parent->Color = Black; z->Parent->Parent->Color = Red; Right_Rotate(T,z->Parent->Parent); } else { y = z->Parent->Parent->Left; if(y->Color = Red) { z->Parent->Color = Black; y->Color = Black; z->Parent->Parent->Color = Red; z = z->Parent->Parent; } else if(z == z->Parent->Left) { z = z->Parent; Left_Rotate(T,z); } z->Parent->Color = Black; z->Parent->Parent->Color = Red; Left_Rotate(T,z->Parent->Parent); } } T->Color = Black; }
刪除操作:
與 n 個結點的紅黑樹上的其他的基本操作一樣,刪除一個結點需要花費 O(lgn) 時間。
下面給出幾種刪除情況:
一、先給出簡單的刪除情況:被刪除結點 A 為 紅色,結點 A 的兄弟和孫子沒有畫出。
這幾種情況可以直接將結點 A 為刪除,紅黑樹性質不會被破壞。
刪除結點 A 后情況如下圖:
二、比較復雜的就是如下左邊的這種圖,因為此時會破壞紅黑性質 5 或可能破壞紅黑性質 4。
讓我們先來分析一下,A 的父結點和子孫結點顏色不確定,用藍色表示。
如若我們刪除 A 結點,則需要尋找一個結點替代結點 A 的位置並變成結點 A 的顏色。
我們可以尋找比 A 小且相鄰的結點,也就是 A 的右子樹中最小的一個結點,用 Min 表示。
我們任然不知道結點 Min 的顏色,這里先分析簡單的,讓它以紅色表示。
因為要保持紅黑性質,所以有如下兩種情況:
這兩種情況只需要簡單的將 Min 結點替換到 A 的位置並將顏色變成 A 的顏色即可。
三、任然是上面左邊兩個圖,當結點 Min 的顏色是黑色時,情況就比較復雜了。
因為當移走黑色的結點 Min 后,會破壞紅黑性質 5,可能會破壞紅黑性質 4。
1、當 Min 結點的右孩子 C 為紅色時的情況如下:
這種情況比較簡單,只需要將 C 結點替換到 Min 結點的位置並將顏色變成黑色即可解決問題。
Min 的兄弟結點只畫了一種情況,其他情況也一樣,但要保持紅黑性質。
2、當 Min 結點的右孩子為黑色時的情況如下:
當 Min 結點刪除后,我們需要找到一個紅結點填到 Min 的那個路徑上,並將顏色變成黑色。
所以當 P 的顏色為紅色時,我們只需要左旋一下 PB,並將 P結點顏色變成黑色即可。
但是當 P 的顏色為黑色時,我們就得在 P 的右子樹中尋找一個紅結點了。
因此我們把這兩種情況和成一種情況,就是把 P 的顏色當作黑色討論。
3.1、對於前三個圖,我們可以歸為一種情況:也就是第二個圖的那種情況:
第二張圖的特點是 Min 結點的兄弟的右孩子 C 為 紅色:
我們先將 PB 左旋,然后顏色互換,再將 C 結點的顏色變成黑色即可。
第三個圖是先將 DC 右旋,然后顏色互換,就變成了第二張圖的情況。
3.2、對於第五個圖其實可以和第四個圖同為一種情況。
我們已經無法在 P 樹的內部尋找到一個合適的紅色結點來替換 Min 的位置了。
所以此時我們得在 P 樹的祖先中尋找一個紅色結點來增加 P 樹的樹高。
我們將 P 結點設為新的起始點,代替原來 Min 的右結點也就是空結點。
當 P 作為新的起始點后,我們需要判斷 P 結點是其父結點的左孩子還是右孩子。
如果是左孩子則執行相同的操作,否則便將該左旋的地方右旋,該右旋的地方左旋,
屬性為 left 的地方變成 right,屬性為 right 的地方變成 left。
總而言之,就是左右互換就對了。最后將起始點顏色變成黑色。
刪除代碼:
1、紅黑樹替換和尋找最小值:
void RB_Transplant(PRBTree &T,PRBTree u,PRBTree v) { if(u->Parent == NULL) T = v; else if(u == u->Parent->Left) u->Parent->Left = v; else u->Parent->Right = v; v->Parent = u->Parent; } PRBTree RBTree_Minimum(PRBTree T) { while(T->Left != NULL) T = T->Left; return T; }
2、紅黑樹刪除:
void RB_Delete(PRBTree &T,PRBTree z) { PRBTree y = z; PRBTree x = NULL; RB_Color Original_Color = y->Color; if(z->Left = NULL) { x = z->Right; RB_Transplant(T,z,z->Right); } else if(z->Right == NULL) { x = z->Left; RB_Transplant(T,z,z->Left); } else { y = RBTree_Minimum(z->Right); Original_Color = y->Color; x = y->Right; if(y->Parent == z) x->Parent = y; else { RB_Transplant(T,y,y->Right); y->Right = z->Right; y->Right->Parent = y; } RB_Transplant(T,z,y); y->Left = z->Left; y->Left->Parent = y; y->Color = z->Color; } if(Original_Color == Black) RB_Delete_Fixup(T,x); }
3、紅黑樹修正:
void RB_Delete_Fixup(PRBTree &T,PRBTree x) { PRBTree w = NULL; while(x != T && x->Color == Black) { if(x == x->Parent->Left) { w = x->Parent->Right; if(w->Color == Red) { w->Color = Black; x->Parent->Color = Red; Left_Rotate(T,x->Parent); w = x->Parent->Right; } if(w->Left->Color == Black && w->Right->Color == Black) { w->Color = Red; x = x->Parent; } else { if(w->Right->Color == Black) { w->Left->Color = Black; w->Color = Red; Right_Rotate(T,w); w = x->Parent->Right; } w->Color = x->Parent->Color; x->Parent->Color = Black; w->Right->Color = Black; Left_Rotate(T,x->Parent); x = T; } } else { w = x->Parent->Left; if(w->Color == Red) { w->Color = Black; x->Parent->Color = Red; Right_Rotate(T,x->Parent); w = x->Parent->Left; } if(w->Right->Color == Black && w->Left->Color == Black) { w->Color = Red; x = x->Parent; } else { if(w->Left->Color == Black) { w->Right->Color = Black; w->Color = Red; Left_Rotate(T,w); w = x->Parent->Left; } w->Color = x->Parent->Color; x->Parent->Color = Black; w->Left->Color = Black; Right_Rotate(T,x->Parent); x = T; } } } x->Color = Black; }
若有錯誤請多擔待,謝謝!