程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 數據結構之紅黑樹,數據結構紅黑

數據結構之紅黑樹,數據結構紅黑

編輯:C++入門知識

數據結構之紅黑樹,數據結構紅黑


  紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹

紅黑樹和AVL樹類似,都是在進行插入和刪除操作時通過特定操作保持二叉查找樹的平衡,
從而獲得較高的查找性能。
它雖然是復雜的,但它的最壞情況運行時間也是非常良好的,並且在實踐中是高效的:
它可以在O(log n)時間內做查找,插入和刪除,這裡的n 是樹中元素的數目。(度娘)
C++ stl裡面的set,map底層就是用紅黑樹實現的。
紅黑樹具體的插入刪除原理請參考<<算法導論>> 維基上面也講得不錯。
反正插入過程就是要解決"紅-紅"沖突,刪除就是要解決"黑-黑"沖突。
下面是按照理我解的方式來實現紅黑樹,我只寫了插入和刪除過程,

其它的操作和普通的二叉樹沒多大區別。。
分享一個可以演示紅黑樹各種操作的網頁:

https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<cstdio> 7 #include<vector> 8 #include<ctime> 9 const int RED= 1; 10 const int BLACK = 0; 11 struct Node { 12 int data; 13 bool color; 14 Node *fa, *left, *right; 15 Node() :color(BLACK), data(0){ fa = left = right = NULL; } 16 Node(int _v, Node *p) :data(_v), color(RED){ fa = left = right = p; } 17 }; 18 struct RedBlackTree{ 19 Node *root, *null; 20 void init(){ 21 null = new Node(); 22 root = null; 23 } 24 /* 25 * 26 * 27 * 28 * 對紅黑樹的節點(x)進行右旋轉 29 * 30 * px px 31 * / / 32 * -> x y 33 * / \ / \ 34 * -> y rx --(右旋)--> ly x 35 * / \ / \ 36 * ly ry ry rx 37 * 38 * 39 * 40 */ 41 // 旋轉操作可以不傳遞引用(reference)話說傳引用會快一些。。。 42 void rotate_right(Node* &x) { 43 Node *y = x->left; 44 x->left = y->right; 45 if (y->right != null) y->right->fa = x; 46 y->fa = x->fa; 47 if (x->fa == null) root = y; 48 else if (x->fa->left == x) x->fa->left = y; 49 else x->fa->right = y; 50 y->right = x; 51 x->fa = y; 52 } 53 // 左旋同上 54 void rotate_left(Node* &x) { 55 Node *y = x->right; 56 x->right = y->left; 57 if (y->left != null) y->left->fa = x; 58 y->fa = x->fa; 59 if (x->fa == null) root = y; 60 else if (x->fa->left == x) x->fa->left = y; 61 else x->fa->right = y; 62 y->left = x; 63 x->fa = y; 64 } 65 Node *find(Node *x, int &data) { 66 while (x != null && x->data != data) { 67 x = data < x->data ? x->left : x->right; 68 } 69 return x; 70 } 71 void insert(int v) { 72 Node *x = root; 73 Node *y = null; 74 while (x != null) { // 先找到帶插入節點的位置,y保存該路徑上最後一個節點 75 y = x; 76 x = v < x->data ? x->left : x->right; 77 } 78 x = new Node(v, null); 79 if (y != null) { // 確定新節點x與其父節點y的大小關系,再將它們連起來 80 if (v < y->data) y->left = x; 81 else y->right = x; 82 } else { 83 root = x; 84 } 85 x->fa = y; 86 insert_fix(x); 87 } 88 void insert_fix(Node* &x) { 89 while (x->fa->color != BLACK) { 90 // x為當前節點 91 // par x的父節點,Gp x的祖父節點, uncle x的叔叔節點 92 Node *par = x->fa, *Gp = par->fa, *uncle = null; 93 if (par == Gp->left) { // 若父節點是祖父的左孩子 94 uncle = Gp->right; // 叔叔為祖父的右孩子 95 if (uncle->color == RED) { // 若叔叔顏色為紅色 96 // Case1:父親和叔叔節點變為紅色,祖父顏色變為紅色 97 // 將祖父節點設置為當前節點,再繼續(因為Gp由黑變成紅色,Gp的父節點可能為紅色) 98 // 所以要繼續向上遞歸調整 99 par->color = BLACK; 100 Gp->color = RED; 101 uncle->color = BLACK; 102 x = Gp; 103 } else if (x == par->right) { 104 // Case2:x為右孩子,將父節點設置為當前節點,再進行左旋 105 // 即變成Case3: 106 rotate_left(x = par); 107 } else { 108 // Case3:x,父節點,祖父節點三者共線(左側) 109 // 將祖父節點改為紅色,父節點改為黑色 110 // 此時調整完成,紅黑樹性質得到恢復 111 Gp->color = RED; 112 par->color = BLACK; 113 rotate_right(Gp); 114 } 115 } else { 116 // 同上,只是由左變成右而已 117 uncle = Gp->left; 118 if (uncle->color == RED) { 119 par->color = BLACK; 120 Gp->color = RED; 121 uncle->color = BLACK; 122 x = Gp; 123 } else if (x == par->left) { 124 rotate_right(x = par); 125 } else { 126 Gp->color = RED; 127 par->color = BLACK; 128 rotate_left(Gp); 129 } 130 } 131 } 132 // 將根置為黑色 133 root->color = BLACK; 134 } 135 void del(int data){ 136 // 先找到待刪除的節點 137 Node *z = find(root, data); 138 // 若沒找到直接退出 139 if (z == null) return; 140 Node *y = z, *x = null; 141 // 若z的左右孩子均不為空,則用y保存z的右子樹中最小的節點 142 if (z->left != null && z->right != null) { 143 y= z->right; 144 while (y->left != null) y = y->left; 145 } 146 // 此時y只有左子樹,或只有右子樹,或左右子樹均不存在 147 // x可能為 null !!! 148 x = y->left != null ? y->left : y->right; 149 // 將x與y的父節點連接起來,這裡可能會改變null節點的fa指針,但沒有關系的。。。 150 x->fa = y->fa; 151 if (y->fa == null) root = x; 152 // 確定y節點與其父親的左右關系 153 else if (y->fa->left == y) y->fa->left = x; 154 else y->fa->right = x; 155 // 若z不等於y 拷貝數據,y才是真正要刪除的節點 156 if (z != y) z->data = y->data; 157 // 如果刪除的節點y的顏色為黑色,對樹進行調整使得樹滿足紅黑樹的要求 158 if (y->color == BLACK) del_fix(x); 159 // 釋放y 160 #ifdef DE_BUG 161 delete y; 162 #endif 163 } 164 void del_fix(Node* &x) { 165 while (x != root && x->color == BLACK) { 166 // x為當前節點 167 // par x的父節點, sibling x的兄弟節點 168 Node *par = x->fa, *sibling = null; 169 // x為父節點的左孩子 170 if (par->left == x){ 171 // 兄弟節點則為父節點的右孩子 172 sibling = par->right; 173 // 若兄弟節點的顏色為紅色 174 if (sibling->color == RED) { 175 // Case1:將兄弟節點染成黑色,父節點染成紅色 176 sibling->color = BLACK; 177 par->color = RED; 178 // 父節點左旋 179 rotate_left(par); 180 // 重新設置兄弟節點 181 sibling = par->right; 182 } else 183 // Case2:兄弟節點為黑色,兄弟節點的左右孩子均為黑色 184 if (sibling->left->color == BLACK && sibling->right->color == BLACK) { 185 // 將兄弟節點染成紅色 186 sibling->color = RED; 187 // 將父節點變成當前節點 188 x = par; 189 } else { 190 // Case3:兄弟節點為黑色,兄弟節點的右孩子為黑色,左孩子為紅色 191 if (sibling->right->color == BLACK) { 192 // 將兄弟節點的左孩子染成黑色 193 sibling->left->color = BLACK; 194 // 將兄弟節點染成紅色 195 sibling->color = RED; 196 // 將兄弟節點右旋 197 rotate_right(sibling); 198 // 重新設置兄弟節點 199 sibling = par->right; 200 } 201 // Case4:兄弟節點為黑色,兄弟節點的右孩子為紅色,左孩子為任意顏色 202 // 將兄弟節點染成父節點的顏色 203 sibling->color = par->color; 204 // 父節點染成黑色 205 par->color = BLACK; 206 // 兄弟節點的右孩子染成黑色 207 sibling->right->color = BLACK; 208 // 將父節點左旋,跳出循環。 209 rotate_left(par); 210 break; 211 } 212 } else { 213 // 同上,只是由左變成右而已 214 sibling = par->left; 215 if (sibling->color == RED) { 216 sibling->color = BLACK; 217 par->color = RED; 218 rotate_right(par); 219 sibling = par->left; 220 } else 221 if (sibling->left->color == BLACK && sibling->right->color == BLACK) { 222 sibling->color = RED; 223 x = par; 224 } else { 225 if (sibling->left->color == BLACK){ 226 sibling->right->color = BLACK; 227 sibling->color = RED; 228 rotate_left(sibling); 229 sibling = par->left; 230 } else { 231 sibling->color = par->color; 232 par->color = BLACK; 233 sibling->left->color = BLACK; 234 rotate_right(par); 235 break; 236 } 237 } 238 } 239 } 240 x->color = BLACK; 241 } 242 }rbt_tree; 243 int main() { 244 //  以下為測試 245 int a = clock(); 246 rbt_tree.init(); 247 for (int i = 0; i < 2000000; i++) { 248 rbt_tree.insert(i); 249 } 250 #ifdef DE_BUG 251 for (int i = 0; i < 2000000; i++) { 252 rbt_tree.del(i); 253 } 254 #endif 255 rbt_tree.del(4); 256 printf("%d\n", clock() - a); 257 return 0; 258 } View Code

下面是以bzoj3224/Tyvj 1728普通平衡樹 為例實現了紅黑樹的各種操作
增加了size域(子樹的大小),cnt域(關鍵字出現的次數)。
不想吐槽了,wa的快吐了%>_<% 由於插入,刪除均用非遞歸方式實現的,

導致維護size,cnt域太麻煩了。。。
自己對拍了數據有輸出1w多行才出錯的(這咋調試啊 (*>﹏<*) )。還好最後發現了bug。

程序只通過了oj的數據,不知道還有沒有隱含的bug →_→ 寫的很搓懶得注釋了。。。
歡迎各位游客批評指正O(∩_∩)O~~

1 #include<algorithm> 2 #include<iostream> 3 #include<cstdlib> 4 #include<cstring> 5 #include<string> 6 #include<cstdio> 7 #include<vector> 8 #include<ctime> 9 const int Max_N = 110000; 10 struct Node { 11 int data, s, c; 12 bool color; 13 Node *fa, *ch[2]; 14 inline void set(int _v, bool _color, int i, Node *p) { 15 data = _v, color = _color, s = c = i; 16 fa = ch[0] = ch[1] = p; 17 } 18 inline void push_up() { 19 s = ch[0]->s + ch[1]->s + c; 20 } 21 inline void push_down() { 22 for (Node *x = this; x->s; x = x->fa) x->s--; 23 } 24 inline int cmp(int v) const { 25 return data == v ? -1 : v > data; 26 } 27 }; 28 struct RedBlackTree { 29 int top; 30 Node *root, *null; 31 Node stack[Max_N], *tail, *store[Max_N]; 32 void init() { 33 tail = &stack[0]; 34 null = tail++; 35 null->set(0, 0, 0, NULL); 36 root = null; 37 top = 0; 38 } 39 inline Node *newNode(int v) { 40 Node *p = null; 41 if (!top) p = tail++; 42 else p = store[--top]; 43 p->set(v, 1, 1, null); 44 return p; 45 } 46 inline void rotate(Node* &x, bool d ) { 47 Node *y = x->ch[!d]; 48 x->ch[!d] = y->ch[d]; 49 if (y->ch[d]->s) y->ch[d]->fa = x; 50 y->fa = x->fa; 51 if (!x->fa->s) root = y; 52 else x->fa->ch[x->fa->ch[0] != x] = y; 53 y->ch[d] = x; 54 x->fa = y; 55 y->s = x->s; 56 x->push_up(); 57 } 58 inline void insert(int v) { 59 Node *x = root, *y = null; 60 while (x->s) { 61 x->s++, y = x; 62 int d = x->cmp(v); 63 if (-1 == d) { 64 x->c++; 65 return; 66 } 67 x = x->ch[d]; 68 } 69 x = newNode(v); 70 if (y->s) y->ch[v > y->data] = x; 71 else root = x; 72 x->fa = y; 73 insert_fix(x); 74 } 75 inline void insert_fix(Node* &x) { 76 while (x->fa->color) { 77 Node *par = x->fa, *Gp = par->fa; 78 bool d = par == Gp->ch[0]; 79 Node *uncle = Gp->ch[d]; 80 if (uncle->color) { 81 par->color = uncle->color = 0; 82 Gp->color = 1; 83 x = Gp; 84 } else if (x == par->ch[d]) { 85 rotate(x = par, !d); 86 } else { 87 Gp->color = 1; 88 par->color = 0; 89 rotate(Gp, d); 90 } 91 } 92 root->color = 0; 93 } 94 inline Node *find(Node *x, int data) { 95 while (x->s && x->data != data) x = x->ch[x->data < data]; 96 return x; 97 } 98 inline void del_fix(Node* &x) { 99 while (x != root && !x->color) { 100 bool d = x == x->fa->ch[0]; 101 Node *par = x->fa, *sibling = par->ch[d]; 102 if (sibling->color) { 103 sibling->color = 0; 104 par->color = 1; 105 rotate(x->fa, !d); 106 sibling = par->ch[d]; 107 } else if (!sibling->ch[0]->color && !sibling->ch[1]->color) { 108 sibling->color = 1, x = par; 109 } else { 110 if (!sibling->ch[d]->color) { 111 sibling->ch[!d]->color = 0; 112 sibling->color = 1; 113 rotate(sibling, d); 114 sibling = par->ch[d]; 115 } 116 sibling->color = par->color; 117 sibling->ch[d]->color = par->color = 0; 118 rotate(par, !d); 119 break; 120 } 121 } 122 x->color = 0; 123 } 124 inline void del(int data) { 125 Node *z = find(root, data); 126 if (!z->s) return; 127 if (z->c > 1) { 128 z->c--; 129 z->push_down(); 130 return; 131 } 132 Node *y = z, *x = null; 133 if (z->ch[0]->s && z->ch[1]->s) { 134 y = z->ch[1]; 135 while (y->ch[0]->s) y = y->ch[0]; 136 } 137 x = y->ch[!y->ch[0]->s]; 138 x->fa = y->fa; 139 if (!y->fa->s) root = x; 140 else y->fa->ch[y->fa->ch[1] == y] = x; 141 if (z != y) z->data = y->data, z->c = y->c; 142 y->fa->push_down(); 143 for (Node *k = y->fa; y->c > 1 && k->s && k != z; k = k->fa) k->s -= y->c - 1; 144 if (!y->color) del_fix(x); 145 store[top++] = y; 146 } 147 inline void kth(int k) { 148 int t; 149 Node *x = root; 150 for (; x->s;) { 151 t = x->ch[0]->s; 152 if (k <= t) x = x->ch[0]; 153 else if (t + 1 <= k && k <= t + x->c) break; 154 else k -= t + x->c, x = x->ch[1]; 155 } 156 printf("%d\n", x->data); 157 } 158 inline void rank(int v) { 159 int t, cur = 0; 160 Node *x = root; 161 for (; x->s;) { 162 t = x->ch[0]->s; 163 if (v == x->data) break; 164 else if (v < x->data) x = x->ch[0]; 165 else cur += t + x->c, x = x->ch[1]; 166 } 167 printf("%d\n", cur + t + 1); 168 } 169 inline void succ(int v) { 170 int ret = 0; 171 Node *x = root; 172 while (x->s) { 173 if (x->data > v) ret = x->data, x = x->ch[0]; 174 else x = x->ch[1]; 175 } 176 printf("%d\n", ret); 177 } 178 inline void pred(int v) { 179 int ret = 0; 180 Node *x = root; 181 while (x->s) { 182 if (x->data < v) ret = x->data, x = x->ch[1]; 183 else x = x->ch[0]; 184 } 185 printf("%d\n", ret); 186 } 187 }rbt; 188 int main() { 189 #ifdef LOCAL 190 freopen("in.txt", "r", stdin); 191 freopen("out.txt", "w+", stdout); 192 #endif 193 rbt.init(); 194 int n, op, v; 195 scanf("%d", &n); 196 while (n--) { 197 scanf("%d %d", &op, &v); 198 if (1 == op) rbt.insert(v); 199 else if (2 == op) rbt.del(v); 200 else if (3 == op) rbt.rank(v); 201 else if (4 == op) rbt.kth(v); 202 else if (5 == op) rbt.pred(v); 203 else rbt.succ(v); 204 } 205 return 0; 206 } View Code

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved