【紅黑樹】的詳細實現(C++)附代碼

紅黑樹的介紹

紅黑樹(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 }

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章