c++ STL 红黑树实现

红黑树是一种自平衡二叉查找树,它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。

 

红黑树应用:
1.linux内核中,进程的虚拟地址区间由红黑树组织管理
2.nginx中,超时时间由红黑树组织管理
3.C++ STL中,C++中set,multiset,map,multimap集合模板类都是在STL红黑树的基础之上实现的
……

 

下面看红黑树在gcc中C++标准库的实现,只关注结点的插入、删除及相应的树平衡操作

I.红黑树的结点

/* gcc-4.4.5/libstdc++-v3/include/bits/stl_tree.h */
/* /usr/include/c++/4.4.4/bits/stl_tree.h */
  85   enum _Rb_tree_color { _S_red = false, _S_black = true };
  86 
  87   struct _Rb_tree_node_base
  88   {
  89     typedef _Rb_tree_node_base* _Base_ptr;
  90     typedef const _Rb_tree_node_base* _Const_Base_ptr;
  91 
  92     _Rb_tree_color      _M_color;
  93     _Base_ptr           _M_parent;
  94     _Base_ptr           _M_left;
  95     _Base_ptr           _M_right;
  96 
  97     static _Base_ptr
  98     _S_minimum(_Base_ptr __x)
  99     {
 100       while (__x->_M_left != 0) __x = __x->_M_left;
 101       return __x;
 102     }
 103 
 104     static _Const_Base_ptr
 105     _S_minimum(_Const_Base_ptr __x)
 106     {
 107       while (__x->_M_left != 0) __x = __x->_M_left;
 108       return __x;
 109     }
 110 
 111     static _Base_ptr
 112     _S_maximum(_Base_ptr __x)
 113     {
 114       while (__x->_M_right != 0) __x = __x->_M_right;
 115       return __x;
 116     }
 117 
 118     static _Const_Base_ptr
 119     _S_maximum(_Const_Base_ptr __x)
 120     {
 121       while (__x->_M_right != 0) __x = __x->_M_right;
 122       return __x;
 123     }
 124   };

 

II.结点的插入
结点的插入主要分以下两个步骤:
1.我们首先以二叉查找树的方法增加节点并标记它为红色。(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。)
2.出现连红时做插入平衡操作

 

i.插入平衡操作(消除连红)
__x:需要做插入平衡操作的结点
__p:__x的父节点
__xpp:__x的祖父节点
__y:__x的叔父节点

 

出现连红(__x,__p为红色,__xpp为黑色)时会有以下三种情况(只看__p是__xpp左儿子的情况)
1.叔父节点是红色,则不能通过翻转/颜色调换使树重新平衡,只能做颜色调换并向祖父节点递归做平衡操作

《c++ STL 红黑树实现》

 

2.叔父节点是黑色,__x是__p的左儿子,通过祖父节点右翻转和颜色调换可使树平衡

《c++ STL 红黑树实现》

 

3.叔父节点是黑色,__x是__p的右儿子,可以通过__p左翻转和颜色调换变成第二种情况,做相应的操作进而使树平衡

《c++ STL 红黑树实现》

 

ii.结点插入实现

/* gcc-4.4.5/libstdc++-v3/src/tree.cc */
160   void
161   _Rb_tree_insert_and_rebalance(const bool          __insert_left,
162                                 _Rb_tree_node_base* __x,
163                                 _Rb_tree_node_base* __p,
164                                 _Rb_tree_node_base& __header)
165   {
166     _Rb_tree_node_base *& __root = __header._M_parent;
167 
168     // Initialize fields in new node to insert.
169     __x->_M_parent = __p;
170     __x->_M_left = 0;
171     __x->_M_right = 0;
172     __x->_M_color = _S_red;
173 
174     // Insert.
175     // Make new node child of parent and maintain root, leftmost and
176     // rightmost nodes.
177     // N.B. First node is always inserted left.
178     if (__insert_left)
179       {
180         __p->_M_left = __x; // also makes leftmost = __x when __p == &__header
181 
182         if (__p == &__header)
183         {
184             __header._M_parent = __x;
185             __header._M_right = __x;
186         }
187         else if (__p == __header._M_left)
188           __header._M_left = __x; // maintain leftmost pointing to min node
189       }
190     else
191       {
192         __p->_M_right = __x;
193 
194         if (__p == __header._M_right)
195           __header._M_right = __x; // maintain rightmost pointing to max node
196       }
/* 当结点与父结点出现连红的情况,需要做树平衡操作;直到树根为止,当向上递归到树根且树根结点是红色时,会修改树根结点为黑色进而增加了树的深度 */
197     // Rebalance.
198     while (__x != __root
199            && __x->_M_parent->_M_color == _S_red)
200       {
201         _Rb_tree_node_base* const __xpp = __x->_M_parent->_M_parent;
202 
203         if (__x->_M_parent == __xpp->_M_left)      /* 父节点是祖父节点的左儿子,父节点是祖父的右儿子与其操作一致,只不过将一些操作由左变成右、由右变成左 */
204           {
205             _Rb_tree_node_base* const __y = __xpp->_M_right; /* 叔父节点 */
206             if (__y && __y->_M_color == _S_red) /* 叔父节点是红色,则不能通过翻转来平衡树,只能向祖父节点递归平衡 */
207               {
208                 __x->_M_parent->_M_color = _S_black;
209                 __y->_M_color = _S_black;
210                 __xpp->_M_color = _S_red;
211                 __x = __xpp;
212               }
213             else /* 叔父节点是黑色,则可以通过翻转来平衡树 */
214               {
215                 if (__x == __x->_M_parent->_M_right) /* 节点是父节点的右儿子,翻转将父节点变成节点的左儿子 */
216                   {
217                     __x = __x->_M_parent;
218                     _Rb_tree_rotate_left(__x, __root);
219                   }
220                 __x->_M_parent->_M_color = _S_black;
221                 __xpp->_M_color = _S_red;
222                 _Rb_tree_rotate_right(__xpp, __root); /* 祖父节点右翻转使树平衡 */
223               }
224           }
225         else
226           {
227             _Rb_tree_node_base* const __y = __xpp->_M_left;
228             if (__y && __y->_M_color == _S_red)
229               {
230                 __x->_M_parent->_M_color = _S_black;
231                 __y->_M_color = _S_black;
232                 __xpp->_M_color = _S_red;
233                 __x = __xpp;
234               }
235             else
236               {
237                 if (__x == __x->_M_parent->_M_left)
238                   {
239                     __x = __x->_M_parent;
240                     _Rb_tree_rotate_right(__x, __root);
241                   }
242                 __x->_M_parent->_M_color = _S_black;
243                 __xpp->_M_color = _S_red;
244                 _Rb_tree_rotate_left(__xpp, __root);
245               }
246           }
247       }
248     __root->_M_color = _S_black;
249   }

 

 

III.结点的删除
结点的删除主要分以下两个步骤:
1.如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题;删除相应的结点
2.删除的结点是黑色时,破坏了树的平衡,则需做删除平衡操作

 

i.删除平衡操作(删除了黑色结点需要做删除平衡操作,通过添加黑色结点来使树重新平衡)
__x:需要做删除平衡操作的结点
__x_parent:__x的父节点
__w:__x的兄弟节点
__wl:__w的左儿子
__wr:__w的右儿子

 

需要删除平衡操作时会有以下五种情况(只看__x是__x_parent左儿子的情况)
1.__x是红色的,则直接将其变成黑色即可
2.__x是黑色或为NULL
  2.1__w是黑色
    2.1.1右儿子是红色,则可通过__w左翻转和颜色调换使树重新平衡

 《c++ STL 红黑树实现》

 

    2.1.2左儿子是红色,则可通过__w右翻转和颜色调换,变成2.1.1的情况
《c++ STL 红黑树实现》

    2.1.3左、右儿子都不是红色,则要做颜色调换,并且向上递归做平衡树操作
《c++ STL 红黑树实现》

  2.2__w是红色
    通过__x_parent的左翻转和颜色调换变成2.1的情况

《c++ STL 红黑树实现》

 

ii.结点删除实现

251   _Rb_tree_node_base*
252   _Rb_tree_rebalance_for_erase(_Rb_tree_node_base* const __z,
253                                _Rb_tree_node_base& __header)
254   {
255     _Rb_tree_node_base *& __root = __header._M_parent;
256     _Rb_tree_node_base *& __leftmost = __header._M_left;
257     _Rb_tree_node_base *& __rightmost = __header._M_right;
258     _Rb_tree_node_base* __y = __z;
259     _Rb_tree_node_base* __x = 0;
260     _Rb_tree_node_base* __x_parent = 0;
261 
262     if (__y->_M_left == 0)     // __z has at most one non-null child. y == z.
263       __x = __y->_M_right;     // __x might be null.
264     else
265       if (__y->_M_right == 0)  // __z has exactly one non-null child. y == z.
266         __x = __y->_M_left;    // __x is not null.
267       else
268         {
269           // __z has two non-null children.  Set __y to
270           __y = __y->_M_right;   //   __z's successor.  __x might be null.
271           while (__y->_M_left != 0)
272             __y = __y->_M_left;
273           __x = __y->_M_right;
274         }
/* 
 * 如果需要删除的节点有两个儿子(为了表述方便,这里所指的儿子,为非叶子节点的儿子),那么问题可以被转化成删除另一个只有一个儿子的节点的问题
 * 通过互换要删除节点的右子树的最小元素与要删除节点的值,再删除右子树的最小元素节点(必定有少于两个非叶子的儿子),这就把问题简化为如何删除最多有一个儿子的节点的问题
 * __y : 实际要删除的节点
 * __x : 实际要删除的节点的儿子(非叶子节点或NULL),__y是黑色则通过__x的路径少了一个黑色结点,所以要平衡的该结点的子树
 */
275     if (__y != __z)                   /* 实际删除的节点与要删除的节点不相同,则将实际删除节点与要删除节点互换,并删除要删除的节点,最后由__y指向实际删除的节点 */
276       {
/* 这里是relink而不是交换节点的值,是因为如果是交换节点的值那么对应对象的地址也会发生变化,通过地址访问的话会出现问题 */
277         // relink y in place of z.  y is z's successor
278         __z->_M_left->_M_parent = __y;
279         __y->_M_left = __z->_M_left;
280         if (__y != __z->_M_right)
281           {
282             __x_parent = __y->_M_parent;
283             if (__x) __x->_M_parent = __y->_M_parent;
284             __y->_M_parent->_M_left = __x;   // __y must be a child of _M_left
285             __y->_M_right = __z->_M_right;
286             __z->_M_right->_M_parent = __y;
287           }
288         else
289           __x_parent = __y;
290         if (__root == __z)
291           __root = __y;
292         else if (__z->_M_parent->_M_left == __z)
293           __z->_M_parent->_M_left = __y;
294         else
295           __z->_M_parent->_M_right = __y;
296         __y->_M_parent = __z->_M_parent;
297         std::swap(__y->_M_color, __z->_M_color);
298         __y = __z;
299         // __y now points to node to be actually deleted
300       }
301     else                             /* 删除结点__z,此时__leftmost或__rightmost可能会发生改变 */
302       {                        // __y == __z
303         __x_parent = __y->_M_parent;
304         if (__x)
305           __x->_M_parent = __y->_M_parent;
306         if (__root == __z)
307           __root = __x;
308         else
309           if (__z->_M_parent->_M_left == __z)
310             __z->_M_parent->_M_left = __x;
311           else
312             __z->_M_parent->_M_right = __x;
313         if (__leftmost == __z)
314           {
315             if (__z->_M_right == 0)        // __z->_M_left must be null also
316               __leftmost = __z->_M_parent;
317             // makes __leftmost == _M_header if __z == __root
318             else
319               __leftmost = _Rb_tree_node_base::_S_minimum(__x);
320           }
321         if (__rightmost == __z)
322           {
323             if (__z->_M_left == 0)         // __z->_M_right must be null also
324               __rightmost = __z->_M_parent;
325             // makes __rightmost == _M_header if __z == __root
326             else                      // __x == __z->_M_left
327               __rightmost = _Rb_tree_node_base::_S_maximum(__x);
328           }
329       }
330     if (__y->_M_color != _S_red)  /* 如果实际删除的结点是红色,则不会改变树的平衡,所以不需要处理;但是要删除的结点是黑色,则会影响树的平衡,所以要做树平衡操作 */
331       {
/* 向上递归平衡__x子树操作(__x子树添加一个黑色结点),直到__x子树平衡(__x为红色或树根) */
332         while (__x != __root && (__x == 0 || __x->_M_color == _S_black))
333           if (__x == __x_parent->_M_left) /* __x是左子树;__x是右子树时操作相同,只不过将右变成左、将左变成右 */
334             {
335               _Rb_tree_node_base* __w = __x_parent->_M_right;  /* __w为平衡子树节点的兄弟 */
/* 如果__w为红色,通过翻转转换成__w是黑色的情况 */
336               if (__w->_M_color == _S_red)
337                 {
338                   __w->_M_color = _S_black;
339                   __x_parent->_M_color = _S_red;
340                   _Rb_tree_rotate_left(__x_parent, __root);
341                   __w = __x_parent->_M_right;
342                 }
/* 此时__w必为黑色 */
343               if ((__w->_M_left == 0 ||
344                    __w->_M_left->_M_color == _S_black) &&
345                   (__w->_M_right == 0 ||
346                    __w->_M_right->_M_color == _S_black))      /* __w的左、右子结点没有一个是红色,则修改兄弟__w的颜色为红色,向上层递归平衡上层子树
347                 {
348                   __w->_M_color = _S_red;
349                   __x = __x_parent;
350                   __x_parent = __x_parent->_M_parent;
351                 }
352               else
353                 {
354                   if (__w->_M_right == 0
355                       || __w->_M_right->_M_color == _S_black)  /* 如果__w的左子结点是红色,通过右翻转转换成__w的右子结点是红色的情况 */
356                     {
357                       __w->_M_left->_M_color = _S_black;
358                       __w->_M_color = _S_red;
359                       _Rb_tree_rotate_right(__w, __root);
360                       __w = __x_parent->_M_right;
361                     }
/* 此时__w的右子结点必为红色 */
362                   __w->_M_color = __x_parent->_M_color;
363                   __x_parent->_M_color = _S_black;
364                   if (__w->_M_right)
365                     __w->_M_right->_M_color = _S_black;
366                   _Rb_tree_rotate_left(__x_parent, __root);
367                   break;
/* 通过翻转后可使树平衡,退出平衡操作 */
368                 }
369             }
370           else
/* 同__x是左子树操作类似 */
371             {
372               // same as above, with _M_right <-> _M_left.
373               _Rb_tree_node_base* __w = __x_parent->_M_left;
374               if (__w->_M_color == _S_red)
375                 {
376                   __w->_M_color = _S_black;
377                   __x_parent->_M_color = _S_red;
378                   _Rb_tree_rotate_right(__x_parent, __root);
379                   __w = __x_parent->_M_left;
380                 }
381               if ((__w->_M_right == 0 ||
382                    __w->_M_right->_M_color == _S_black) &&
383                   (__w->_M_left == 0 ||
384                    __w->_M_left->_M_color == _S_black))
385                 {
386                   __w->_M_color = _S_red;
387                   __x = __x_parent;
388                   __x_parent = __x_parent->_M_parent;
389                 }
390               else
391                 {
392                   if (__w->_M_left == 0 || __w->_M_left->_M_color == _S_black)
393                     {
394                       __w->_M_right->_M_color = _S_black;
395                       __w->_M_color = _S_red;
396                       _Rb_tree_rotate_left(__w, __root);
397                       __w = __x_parent->_M_left;
398                     }
399                   __w->_M_color = __x_parent->_M_color;
400                   __x_parent->_M_color = _S_black;
401                   if (__w->_M_left)
402                     __w->_M_left->_M_color = _S_black;
403                   _Rb_tree_rotate_right(__x_parent, __root);
404                   break;
405                 }
406             }
407         if (__x) __x->_M_color = _S_black;
408       }
409     return __y;
410   }

 

    原文作者:算法小白
    原文地址: https://blog.csdn.net/Morphad/article/details/9799517
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞