续 "关于sgi stl为什么使用rb-tree的想法"

   这两天继续研究RB_Tree。

   上次说到为什么sgi stl会选择rb_tree作为set,map的基础实现,而不选择avl_tree。经过对资料的查询,最终,得出的结论是rb_tree在rebalance方面右性能优势。rb_tree的rebalance的时间顶多是进行两次totate,而avl_tree就需要有O(log(N))的时间。
   那么,这次我们先来研究为什么rb_tree的rebalance函数只需要2次rotate就能够解决。
   其实问题的答案就在sgi版本的stl源码中,让我们对stl中stl_tree.h进行解析,就不难找出答案。
   sgi版本的stl中stl_tree.h实现了rb_tree的迭代器,数据结构以及各种操作。
   为了更大的弹性,sgi将rb_tree的节点结构以及迭代器都实现为两层(与slist类似)。如图所示:

   而设计中四个类全部使用struct进行设计,所以所有成员都是public的。

   进行插入操作之后,rb_tree都会进行一次调整操作,将树的状态调整到符合rb_tree的要求。此时就会调用我们今天的主角__Rb_tree_rebalance()函数。此函数解析如下:

None.gif   // 全局函数
None.gif  
// 重新令树形平衡(改变颜色及旋转树形)
None.gif  
// 第一个参数为新增节点
None.gif  
// 第二个参数为root
None.gif
  inline  void  
None.gif  _Rb_tree_rebalance(_Rb_tree_node_base
*  __x, _Rb_tree_node_base *&  __root)
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    __x
->_M_color = _M_red;                       //新节点的颜色一定是红色
InBlock.gif
    while (__x != __root 
InBlock.gif       
&& __x->_M_parent->_M_color == _M_red)     //父节点为红
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif    
if (__x->_M_parent == __x->_M_parent->_M_parent->_M_left)    //父节点为祖父节点之左子节点
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        _Rb_tree_node_base
* __y = __x->_M_parent->_M_parent->_M_right;    //令__y为伯父节点
InBlock.gif
        if (__y && __y->_M_color == _M_red)                               //伯父节点存在而且为红
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif{
InBlock.gif        __x
->_M_parent->_M_color = _M_black;                              //将父节点的颜色改成黑色
InBlock.gif
        __y->_M_color = _M_black;                                          //将伯父节点的颜色改成黑色
InBlock.gif
        __x->_M_parent->_M_parent->_M_color = _M_red;                     //将祖父节点的颜色改成红色
InBlock.gif
        __x = __x->_M_parent->_M_parent;                                  //将__x设置成祖父节点
ExpandedSubBlockEnd.gif
          }

InBlock.gif        
else                     //如果没有伯父节点,或伯父节点为黑色
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif
InBlock.gif        
if (__x == __x->_M_parent->_M_right)          //如果新节点是父节点的右子节点
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif{
InBlock.gif            __x 
= __x->_M_parent;                     //将新节点赋值成其父节点
InBlock.gif
            _Rb_tree_rotate_left(__x, __root);        //将树做左旋转,第一个参数是左旋点
ExpandedSubBlockEnd.gif
          }

InBlock.gif        __x
->_M_parent->_M_color = _M_black;          //将父节点的颜色更改成黑色
InBlock.gif
        __x->_M_parent->_M_parent->_M_color = _M_red;  //将祖父节点的颜色更改成红色
InBlock.gif
        _Rb_tree_rotate_right(__x->_M_parent->_M_parent, __root);  //将树做右旋转,第一个参数是右旋点
ExpandedSubBlockEnd.gif
          }

ExpandedSubBlockEnd.gif      }

InBlock.gif    
else          //如果父节点是祖父节点之右子节点
ExpandedSubBlockStart.gifContractedSubBlock.gif
      dot.gif{
InBlock.gif        _Rb_tree_node_base
* __y = __x->_M_parent->_M_parent->_M_left;      //令__y为伯父节点
InBlock.gif
        if (__y && __y->_M_color == _M_red)        //如果伯父节点存在而且伯父节点的颜色是红色
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif{
InBlock.gif        __x
->_M_parent->_M_color = _M_black;       //将父节点颜色改成黑色
InBlock.gif
        __y->_M_color = _M_black;                  //将伯父节点颜色改成黑色
InBlock.gif
        __x->_M_parent->_M_parent->_M_color = _M_red;  //将祖父节点的颜色改成红色
InBlock.gif
        __x = __x->_M_parent->_M_parent;             //将新节点赋值成祖父节点
ExpandedSubBlockEnd.gif
          }

InBlock.gif        
else                   //如果没有祖父节点,或者祖父节点颜色是黑色
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif{
InBlock.gif        
if (__x == __x->_M_parent->_M_left)     //如果新节点是父节点的左子节点
ExpandedSubBlockStart.gifContractedSubBlock.gif
          dot.gif{
InBlock.gif            __x 
= __x->_M_parent;             //将父节点的值赋值给新节点
InBlock.gif
            _Rb_tree_rotate_right(__x, __root);   //将树做右旋转,第一个参数是右旋点
ExpandedSubBlockEnd.gif
          }

InBlock.gif        __x
->_M_parent->_M_color = _M_black;     //将父节点的颜色改成黑色
InBlock.gif
        __x->_M_parent->_M_parent->_M_color = _M_red;    //将祖父节点的颜色改成红色
InBlock.gif
        _Rb_tree_rotate_left(__x->_M_parent->_M_parent, __root);    //将树做左旋转,第一个参数是左旋点
ExpandedSubBlockEnd.gif
          }

ExpandedSubBlockEnd.gif      }

ExpandedSubBlockEnd.gif      }

InBlock.gif    __root
->_M_color = _M_black;        //根节点永远都是黑色(符合RB_Tree定义)
ExpandedBlockEnd.gif
  }

None.gif
None.gif

其中还会调用另外两个全局函数做左旋转以及右旋转。函数代码分析如下:
None.gif // 全局函数
None.gif  
// 新节点必定是红节点,如果插入处的父节点也是红节点,就违反了红黑树规则,此时必须
None.gif  
// 对红黑树进行旋转(以及改变颜色)
None.gif  
// 此函数为左旋转函数
None.gif  
// 第一个参数是左旋点
None.gif  
// 第二个参数是root
None.gif
   inline  void  
None.gif  _Rb_tree_rotate_left(_Rb_tree_node_base
*  __x, _Rb_tree_node_base *&  __root)
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    _Rb_tree_node_base
* __y = __x->_M_right;            //令__y作为左旋点的右子节点
InBlock.gif
    __x->_M_right = __y->_M_left;                       
InBlock.gif    
if (__y->_M_left !=0)
InBlock.gif      __y
->_M_left->_M_parent = __x;                           //设定父节点
InBlock.gif
    __y->_M_parent = __x->_M_parent;
InBlock.gif    
InBlock.gif
InBlock.gif    
//令__y完全顶替__x的地位
InBlock.gif
    if (__x == __root)                  //__x为根节点
InBlock.gif
      __root = __y;
InBlock.gif    
else if (__x == __x->_M_parent->_M_left)           //__x为其父节点的左子节点
InBlock.gif
      __x->_M_parent->_M_left = __y;
InBlock.gif    
else                                                //__x为其父节点的右子节点
InBlock.gif
      __x->_M_parent->_M_right = __y;                 
InBlock.gif    __y
->_M_left = __x;
InBlock.gif    __x
->_M_parent = __y;
ExpandedBlockEnd.gif  }

None.gif
None.gif
None.gif  
// 全局函数
None.gif  
// 新节点必定是红节点,如果插入处的父节点也是红节点,就违反了红黑树规则,此时必须
None.gif  
// 对红黑树进行旋转(以及改变颜色)
None.gif  
// 此函数为右旋转函数
None.gif  
// 第一个参数是右旋点
None.gif  
// 第二个参数是root
None.gif
  inline  void  
None.gif  _Rb_tree_rotate_right(_Rb_tree_node_base
*  __x, _Rb_tree_node_base *&  __root)
ExpandedBlockStart.gifContractedBlock.gif  
dot.gif {
InBlock.gif    _Rb_tree_node_base
* __y = __x->_M_left;             //令__y作为右旋点的左子节点
InBlock.gif
    __x->_M_left = __y->_M_right;
InBlock.gif    
if (__y->_M_right != 0)
InBlock.gif      __y
->_M_right->_M_parent = __x;                   //设定父节点
InBlock.gif
    __y->_M_parent = __x->_M_parent;
InBlock.gif
InBlock.gif
InBlock.gif    
//令__y完全顶替__x的地位
InBlock.gif
    if (__x == __root)                          //__x为根节点
InBlock.gif
      __root = __y;
InBlock.gif    
else if (__x == __x->_M_parent->_M_right)    //__x为其父节点的左子节点
InBlock.gif
      __x->_M_parent->_M_right = __y;
InBlock.gif    
else
InBlock.gif      __x
->_M_parent->_M_left = __y;             //__x为其父节点的右子节点
InBlock.gif
    __y->_M_right = __x;
InBlock.gif    __x
->_M_parent = __y;
ExpandedBlockEnd.gif  }

综观__rb_tree_rebalance的实现代码,我们能够清晰的看到,为了使树形平衡,有时候只许改变节点颜色,有时候需要做单旋转,有时候需要做双旋转(两次单旋转),有时候需要左旋,有时候需要右旋。
所以,rb_tree的rebalance最多只需要两次rotate即可达到目的。

以上分析也印证了一个道理:源码面前,了无秘密。

参考资料:《stl 源码剖析》  侯捷 著

转载于:https://www.cnblogs.com/skipper-haobaobao/archive/2007/03/31/695340.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值