紅黑樹的介紹
紅黑樹(Red-Black Tree,簡稱R-B Tree),它一種特殊的二叉查找樹。
紅黑樹是特殊的二叉查找樹,意味着它滿足二叉查找樹的特徵:任意一個節點所包含的鍵值,大於等於左孩子的鍵值,小於等於右孩子的鍵值。
除了具備該特性之外,紅黑樹還包括許多額外的信息。紅黑樹的每個節點上都有存儲位表示節點的顏色,顏色是紅(Red)或黑(Black)。
紅黑樹一棵在每個結點上增加了一個存儲位來表示結點顏色(紅色RED或黑色BLACK)的二叉搜索樹。
二叉搜索樹簡單的說就是:對樹中任何結點x,其左子樹中的關鍵字最大不超過x.key,即對左子樹中任一結點y,有y.key<x.key;其右子樹中的關鍵字最小不低於x.key,即對右子樹中任一結點y,有y.key>x.key。
紅黑樹中每個結點包含5個屬性:color、key、left、right和p。如果一個結點沒有子結點或父結點,則該結點相應屬性值爲NIL。如圖1所示:
圖1 紅黑樹的葉結點
這些NIL被視爲指向二叉搜索樹的葉結點(外部結點)的指針,而把帶關鍵字的結點視爲樹的內部結點。
爲了便於處理紅黑樹代碼中的邊界條件,使用一個哨兵T.nil來代表所有的NIL:所有的葉結點和根結點的父結點。如圖2所示:
圖2 紅黑樹的T.nil屬性
紅黑樹的特性:
(1) 每個節點或者是黑色,或者是紅色。
(2) 根節點是黑色。
(3) 每個葉子節點是黑色(爲空的結點)。
(4) 不能出現兩個連續的紅色結點(如果一個節點是紅色的,那麼它的兩個子節點都是黑色的)。
(5) 從一個節點開始所有路徑上包含相同數目的黑節點。
關於它的特性,需要注意的是:
第一,特性(3)中的葉子節點,是隻爲空(NIL或null)的節點。
第二,特性(5),確保沒有一條路徑會比其他路徑長出兩倍。因而,紅黑樹是相對是接近平衡的二叉樹。
紅黑樹示意圖如下:
從紅黑樹中任一結點x出發(不包括結點x),到達一個外部結點的任一路徑上的黑結點個數叫做結點x的黑高度,亦稱爲結點的階(rank),記作bh(x)。紅黑樹的黑高度定義爲其根結點的黑高度。下圖,數字表示該結點的黑高度。
紅黑樹的搜索
由於每一棵紅黑樹都是二叉搜索樹,可以使用與搜索普通二又搜索樹時所使用的完全相同的算法進行搜索。在搜索過程中不需使用顏色信息。
對普通二又搜索樹進行搜索的時間複雜性爲O(h),對於紅黑樹則爲O(log2n)。因爲在搜索普通二又搜索樹、AVL樹和紅黑樹時使用了相同的代碼,並且在最差情況下AVL樹的高度最小,因此,在那些以搜索操作爲主的應用程序中,最差情況下AVL樹能獲得最優的時間複雜性。
C/C++Linux服務器開發精彩內容包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒體,P2P,Linux內核,Docker,TCP/IP,協程,DPDK多個高級知識點
學習視頻資料點擊:C/C++Linux服務器開發/Linux後臺架構師-學習視頻
C/C++Linux後臺服務器開發技術:交流qun
紅黑樹的插入
首先使用二又搜索樹的插入算法將一個元素插入到紅黑樹中,該元素將作爲新的葉結點插入到某一外部結點位置。在插入過程中需要爲新元素染色。
如果插入前是空樹,那麼新元素將成爲根結點,根結點必須染成黑色。
如果插入前樹非空,若新結點被染成黑色,將違反紅黑樹的特性,所有從根到外部結點的路徑上的黑色結點個數不等。因此,新插入的結點將染成紅色,但這又可能違反紅黑樹的特性,出現連續兩個紅色結點,因此需要重新平衡。
>>設新插入的結點爲u,它的父結點和祖父結點分別是pu和gu。
>若pu是黑色結點,再插入紅色結點,則特性沒有破壞,結束重新平衡的過程。
>若pu是紅色結點,則出現連續兩個紅色結點的情形,這時還要考查pu的兄弟結點。
情況1:如果pu的兄弟結點gr是紅色結點,此時結點pu的父結點gu是黑色結點,它有兩個紅色子女結點。交換結點gu和它的子女結點的顏色。
情況2:如果pu的兄弟結點gr是黑色結點,此時又有兩種情況。
(1)u是pu的左子女,pu是gu的左子女。在這種情況下只要對pu做一次右單旋轉,交換pu和gu的顏色,就可恢復紅黑樹的特性,並結束重新平衡過程。
(2)u是pu的右子女,pu是gu的左子女。在這種情況下對pu做先左後右的雙旋轉,再交換u與gu的顏色,就可恢復紅黑樹的特性,結束重新平衡過程。
>當結點u是pu的右子女的情形與u是pu的左子女的情形是鏡像的,只要左右指針互換即可。
紅黑樹的刪除
紅黑樹的刪除算法與二又搜索樹的刪除算法類似,不同之處在於,在紅黑樹中執行一次二叉搜索樹的刪除運算,可能會破壞紅黑樹的特性,需要重新平衡。
在紅黑樹中真正刪除的結點應是葉結點或只有一個子女的結點。若設被刪除爲p,其唯一的子女爲s。結點p被刪除後,結點s取代了它的位置。
如果被刪結點p是紅色的,刪去它不存在問題。因爲樹中各結點的黑高度都沒有改變,也不會出現連續兩個紅色結點,紅黑樹的特性仍然保持,不需執行重新平衡過程。
如果被刪結點p是黑色的,一旦刪去它,紅黑樹將不滿足特性的要求,因爲在這條路徑上黑色結點少了一個,從根到外部結點的黑高度將會降低。因此必須通過旋轉變換和改變結點的顏色,消除雙重黑色結點,恢復紅黑樹的特性。
>>設u是被刪結點p的唯一的子女結點。
>如果u是紅色結點,可以把結點u染成黑色,從而恢復紅黑樹的特性。
>如果被刪結點p是黑色結點,它的唯一的子女結點u也是黑色結點,就必須先將結點p摘下,將結點u鏈到其祖父結點g的下面。假設結點u成爲結點g的右子女,v是u的左兄弟。根據v的顏色,分以下兩種情況討論:
情況1:結點v是黑色結點,若設結點v的左子女結點爲w。根據w的顏色又需分兩種情況討論:
(1)結點w是紅色結點,此時作一次右單旋轉,將w、g染成黑色,v染成紅色,如圖所示,就可消除結點u的雙重黑色,恢復紅黑樹的性質。
(2)結點w是黑色結點,還要看結點w的右兄弟結點r。根據結點r的顏色,又要分兩種情況:
①結點r是紅色結點,可通過一次先左後右的雙旋轉,並將g染成黑色,就可消除結點u的雙重黑色,恢復紅黑樹的特性。
②結點r是黑色結點,這時還要看結點g的顏色。如果g是紅色結點,只要交換結點g和其子女結點v的顏色就能恢復紅黑樹的特性,如圖(a)。如果g是黑色結點,可做一次右單旋轉,如圖(b)。
情況2:結點υ是紅色結點。考查v的右子女結點r。根據紅黑樹的特性,r一定是黑色結點。再看結點r的左子女結點s。根據s的顏色,可以分爲兩種情況討論。
(1)結點s是紅色結點。通過一次先左後右雙旋轉,讓r上升,使包含u的路徑的黑高度增1,從而消除結點u的雙重黑色,恢復紅黑樹的特性。
(2)結點s是黑色結點,再看結點s的右兄弟結點t。根據結點t的顏色又可分爲兩種情況進行討論。
①若結點t爲紅色結點,先以t爲旋轉軸,做左單旋轉,以t替補r的位置;然後再以t爲旋轉軸,做一次先左後右的雙旋轉,可消除結點u的雙重黑色,恢復紅黑樹的特性。
②若結點t爲黑色結點,以v爲旋轉軸,做一次右單旋轉,並改變υ和r的顏色,即可消除結點u的雙重黑色,恢復紅黑樹的特色。
>當結點u是結點g的左子女的情況與上面討論的情況是鏡像的,只要左、右指針互換就可以了。
紅黑樹的實現代碼
1 typedef enum { RED = 0, BLACK } Color;
2 //紅黑樹結點類型
3 template <typename Type>
4 struct RBTNode
5 {
6 Color color; //顏色
7 Type key; //關鍵字
8 RBTNode* left; //左孩子
9 RBTNode* right; //右孩子
10 RBTNode* parent; //父結點
11 };
12
13 //紅黑樹類型
14 template<typename Type>
15 class RBTree
16 {
17 public:
18 //構造函數
19 RBTree()
20 {
21 Nil = BuyNode();
22 root = Nil;
23 Nil->color = BLACK;
24 }
25 //析構函數
26 ~RBTree()
27 {
28 destroy(root); //銷燬創建的非Nil結點
29 delete Nil; //最後刪除Nil結點
30 Nil = NULL;
31 }
32
33 //中序遍歷
34 void InOrder() { InOrder(root); }
35
36 //插入
37 //1.BST方式插入
38 //2.調整平衡
39 bool Insert(const Type &value)
40 {
41 RBTNode<Type>* pr = Nil; //pr用來記住父節點
42 RBTNode<Type>* s = root; //定義變量s指向根
43 while (s != Nil)
44 {
45 if (value == s->key)
46 {
47 return false;
48 }
49 pr = s; //每次記住s的父節點
50 if (value < s->key)
51 {
52 s = s->left;
53 }
54 else
55 {
56 s = s->right;
57 }
58 }
59 //循環後s==Nil
60 s = BuyNode(value); //申請結點
61 if (pr == Nil) //如果父節點pr是根節點,第一次root指向Nil,所以pr==Nil
62 {
63 root = s;
64 root->parent = pr;
65 }
66 else //如果父節點不是根節點
67 {
68 if (value < pr->key)
69 {
70 pr->left = s;
71 }
72 else
73 {
74 pr->right = s;
75 }
76 s->parent = pr; //設置新結點s的父節點
77 }
78 //調整平衡
79 Insert_Fixup(s);
80 return true;
81 }
82
83 //刪除key結點(先查找,再調用內部刪除)
84 void Remove(Type key)
85 {
86 RBTNode<Type>* t;
87 if ((t = Search(root, key)) != Nil)
88 {
89 Remove(t);
90 }
91 else
92 {
93 cout << "Key is not exist." << endl;
94 }
95 }
96
97 //中序遍歷打印結點詳細的結點顏色
98 void InOrderPrint() { InOrderPrint(root); }
99
100 protected:
101 //申請結點結點,將結點的顏色初始化爲紅色,初始化結點的關鍵字,其他的初始化爲空
102 RBTNode<Type>* BuyNode(const Type &x = Type())
103 {
104 RBTNode<Type>* s = new RBTNode<Type>();
105 assert(s != NULL);
106 s->color = RED;
107 s->left = s->right = s->parent = Nil;
108 s->key = x;
109 return s;
110 }
111
112 //中序遍歷
113 void InOrder(RBTNode<Type>* root)
114 {
115 if (root != Nil)
116 {
117 InOrder(root->left);
118 cout << root->key << " ";
119 InOrder(root->right);
120 }
121 }
122
123 //左轉,對z結點左轉
124 // zp zp
125 // / \
126 // z z
127 // / \ / \
128 // lz y lz y
129 // / \ / \
130 // ly ry ly ry
131 void LeftRotate(RBTNode<Type>* z)
132 {
133 RBTNode<Type>* y = z->right; //用y指向要轉動的z結點
134 z->right = y->left;
135 if (y->left != Nil) //y所指結點的左結點不爲空
136 {
137 y->left->parent = z;
138 }
139 y->parent = z->parent;
140 if (root == z) //z就是根節點
141 {
142 root = y;
143 }
144 else if (z == z->parent->left) //z在左結點
145 {
146 z->parent->left = y;
147 }
148 else //z在右結點
149 {
150 z->parent->right = y;
151 }
152 y->left = z;
153 z->parent = y;
154 }
155
156 //右轉,對z結點進行右轉
157 // zp zp
158 // / \
159 // z z
160 // / \ / \
161 // y rz y rz
162 // / \ / \
163 // ly ry ly ry
164 void RightRotate(RBTNode<Type>* z)
165 {
166 RBTNode<Type>* y = z->left;
167 z->left = y->right;
168 if (y->right != Nil)
169 {
170 y->right->parent = z;
171 }
172 y->parent = z->parent;
173 if (root == z) //如果z是根結點
174 {
175 root = y;
176 }
177 else if (z == z->parent->left) //z在左結點
178 {
179 z->parent->left = y;
180 }
181 else //z在右結點
182 {
183 z->parent->right = y;
184 }
185 y->right = z;
186 z->parent = y;
187 }
188
189 //插入後的調整函數
190 void Insert_Fixup(RBTNode<Type>* s)
191 {
192 RBTNode<Type>* uncle; //叔結點(父結點的兄弟結點)
193 while (s->parent->color == RED) //父節點的顏色也爲紅色
194 {
195 if (s->parent == s->parent->parent->left) //父節點是左結點
196 {
197 uncle = s->parent->parent->right;
198
199 if (uncle->color == RED) //叔結點爲紅色
200 {
201 //父節點和叔結點都變爲黑色
202 s->parent->color = BLACK;
203 uncle->color = BLACK;
204 //祖父結點變爲紅色
205 s->parent->parent->color = RED;
206 //將s指針指向祖父結點,下一次循環繼續判斷祖父的父節點是否爲紅色
207 s = s->parent->parent;
208 }
209 else //沒有叔結點,或叔結點爲黑色(經過多次循環轉換,叔結點可能爲黑)
210 {
211 if (s == s->parent->right) //如果調整的結點在右結點
212 {
213 s = s->parent; //先將s指向s的父結點
214 LeftRotate(s); //再左轉
215 }
216 //如果調整的結點在左結點,將s的父節點變爲黑色,將祖父的結點變爲紅色,將s的祖父結點右轉
217 s->parent->color = BLACK;
218 s->parent->parent->color = RED;
219 RightRotate(s->parent->parent);
220 }
221 }
222 else
223 {
224 if (s->parent == s->parent->parent->right) //父節點是右結點
225 {
226 uncle = s->parent->parent->left;
227 if (uncle->color == RED) //叔結點爲紅色
228 {
229 //父節點和叔結點都變爲黑色
230 s->parent->color = BLACK;
231 uncle->color = BLACK;
232 //祖父結點變爲紅色
233 s->parent->parent->color = RED;
234 //將s指針指向祖父結點,下一次循環繼續判斷祖父的父節點是否爲紅色
235 s = s->parent->parent;
236 }
237 else //沒有叔結點,或叔結點爲黑色(經過多次循環轉換,叔結點可能爲黑)
238 {
239 if (s == s->parent->left) //如果調整的結點在左結點
240 {
241 s = s->parent; //先將s指向s的父結點
242 RightRotate(s); //再右轉
243 }
244 //如果調整的結點在右結點,將s的父節點變爲黑色,將祖父的結點變爲紅色,將s的祖父結點右轉
245 s->parent->color = BLACK;
246 s->parent->parent->color = RED;
247 LeftRotate(s->parent->parent);
248 }
249 }
250 }
251 }
252 root->color = BLACK; //最後始終將根節點置爲黑色
253 }
254
255 //查找key結點
256 RBTNode<Type>* Search(RBTNode<Type>* root, Type key) const
257 {
258 if (root == Nil) //root爲空,或key和根的key相同
259 {
260 return Nil;
261 }
262
263 if (root->key == key)
264 {
265 return root;
266 }
267 if (key<root->key)
268 {
269 return Search(root->left, key);
270 }
271 else
272 {
273 return Search(root->right, key);
274 }
275 }
276
277 //將u的子節點指針指向u改變指向v,將v的父節點改變爲指向u的父節點
278 // up
279 // \
280 // u
281 // / \
282 // ul ur
283 // / \
284 // v ulr
285 // \
286 // rv
287 void Transplant(RBTNode<Type>* u, RBTNode<Type>* v)
288 {
289 if (u->parent == Nil) //u的父節點爲空
290 {
291 root = v; //直接令根root爲v
292 }
293 else if (u == u->parent->left) //u父節點不爲空,且u在左子樹
294 {
295 u->parent->left = v;
296 }
297 else //u在左子樹
298 {
299 u->parent->right = v;
300 }
301 v->parent = u->parent;
302 }
303
304 //找到最左結點(最小)
305 // xp
306 // \
307 // x
308 // / \
309 // xl xr
310 // / \
311 // xll xlr
312
313 RBTNode<Type>* Minimum(RBTNode<Type>* x)
314 {
315 if (x->left == Nil)
316 {
317 return x;
318 }
319 return Minimum(x->left);
320 }
321
322 //刪除紅黑樹結點z
323 void Remove(RBTNode<Type>* z)
324 {
325 RBTNode<Type>* x = Nil;
326 RBTNode<Type>* y = z; //y記住傳進來的z結點
327 Color ycolor = y->color; //
328 if (z->left == Nil) //z只有右孩子
329 {
330 x = z->right;
331 Transplant(z, z->right);
332 }
333 else if (z->right == Nil) //z只有右孩子
334 {
335 x = z->left;
336 Transplant(z, z->left);
337 }
338 else //右左孩子和右孩子
339 {
340 y = Minimum(z->right); //y是z右子樹的的最左子樹
341 ycolor = y->color;
342 x = y->right;
343 if (y->parent == z) //z的右子結點沒有左節點或爲Nil
344 {
345 x->parent = y;
346 }
347 else //z的右子結點有左節點或爲Nil
348 {
349 Transplant(y, y->right);
350 y->right = z->right;
351 y->right->parent = y;
352 }
353 Transplant(z, y);
354 //改變指向
355 y->left = z->left;
356 z->left->parent = y;
357 y->color = z->color;
358 }
359 if (ycolor == BLACK)
360 {
361 Remove_Fixup(x);
362 }
363 }
364
365 //紅黑樹刪除調整
366 void Remove_Fixup(RBTNode<Type>* x)
367 {
368 while (x != root&&x->color == BLACK) //當結點x不爲根並且它的顏色不是黑色
369 {
370 if (x == x->parent->left) //x在左子樹
371 {
372 RBTNode<Type>* w = x->parent->right; //w是x的兄結點
373
374 if (w->color == RED) //情況1
375 {
376 w->color = BLACK;
377 x->parent->color = RED;
378 LeftRotate(x->parent);
379 w = x->parent->right;
380 }
381 if (w->left->color == BLACK&&w->right->color == BLACK) //情況2
382 {
383 w->color = RED;
384 x = x->parent;
385 }
386 else
387 {
388 if (w->right->color == BLACK) //情況3
389 {
390 w->color = RED;
391 w->left->color = BLACK;
392 RightRotate(w);
393 w = x->parent->right;
394 }
395 //情況4
396 w->color = w->parent->color;
397 w->parent->color = BLACK;
398 w->right->color = BLACK;
399 LeftRotate(x->parent);
400 x = root; //結束循環
401
402 }
403 }
404 else //x在右子樹
405 {
406 RBTNode<Type>* w = x->parent->left;
407 if (w->color == RED) //情況1
408 {
409 w->parent->color = RED;
410 w->color = BLACK;
411 RightRotate(x->parent);
412 w = x->parent->left;
413 }
414 if (w->right->color == BLACK&&w->right->color == BLACK) //情況2
415 {
416 w->color = RED;
417 x = x->parent;
418 }
419 else
420 {
421 if (w->left->color == BLACK) //情況3
422 {
423 w->right->color = BLACK;
424 w->color = RED;
425 LeftRotate(w);
426 w = x->parent->left;
427 }
428 //情況4
429 w->color = x->parent->color;
430 x->parent->color = BLACK;
431 w->left->color = BLACK;
432 RightRotate(x->parent);
433 x = root; //結束循環
434 }
435 }
436 }
437 x->color = BLACK;
438 }
439
440 //銷燬紅黑樹
441 void destroy(RBTNode<Type>* &root)
442 {
443 if (root == Nil)
444 {
445 return;
446 }
447 if (root->left != Nil)
448 {
449 destroy(root->left);
450 }
451 if (root->right != Nil)
452 {
453 destroy(root->right);
454 }
455 delete root;
456 root = NULL;
457 }
458
459 //中序遍歷打印結點詳細的結點顏色
460 void InOrderPrint(RBTNode<Type>* node)
461 {
462 if (node == Nil)
463 {
464 return;
465 }
466 if (node->left != NULL)
467 {
468 InOrderPrint(node->left);
469 }
470 cout << node->key << "(" << ((node->color == BLACK) ? "BLACK" : "RED") << ")" << " ";
471 if (node->right != Nil)
472 {
473 InOrderPrint(node->right);
474 }
475 }
476
477 private:
478 RBTNode<Type>* root; //根指針
479 RBTNode<Type>* Nil; //外部結點,表示空結點,黑色的
480 };
測試代碼
1 int main(int argc, char* argv[])
2 {
3 RBTree<int> rb;
4 // rb.InitTree();
5 int arr[] = { 10,7,8,15,5,6,11,13,12 };
6 int n = sizeof(arr) / sizeof(int);
7 for (int i = 0; i < n; i++)
8 {
9 rb.Insert(arr[i]);
10 }
11
12 rb.InOrder();
13 cout << endl;
14 rb.InOrderPrint();
15 cout << endl;
16 rb.Remove(10);
17 rb.InOrder();
18 cout << endl;
19 rb.Remove(21);
20 return 0;
21 }