C++ 中的红黑树

标准的STL关联式容器可分为两大类set(集合)和map(映射表/字典),以及这两大类的衍生体multiset和multimap。一般而言,关联式容器的内部结构都是一个平衡二叉树,以便获得良好的搜寻效率。平衡二叉树有很多种类型,其中最被广泛运用于STL的是RB-tree(红黑树)。RB-tree也是一个独立容器,但并不开放给外界使用。

此外 SGI STL 还提供了一个不在标砖规格之外的关联式容器:hash_table(散列表),以及以此hashtable为底层机制而完成的hash_set、hash_map、hash_multiset、hash_multimap。

红黑树必须满足以下规则

  1. 每个节点不是红色就是黑色
  2. 根节点为黑色
  3. NULL节点看作黑色。
  4. 如果节点为红,其子节点必为黑色
  5. 任意节点至NULL(树尾端)的任何路径,所含的黑节点个数必须相同(确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树)。

根据规则4,新增节点必须为红,根据规则3,新增节点的父节点必须为黑;当新节点根据二叉搜索树的规则达到其插入点却未能符合上述条件时,就必须调整颜色并旋转树形。

插入节点

假设我们要为上面的红黑树插入3,8,35,75 四个节点。因为新增节点必为红色,则不论插入哪一个节点,都会破坏红黑树的规则,所以我们必须旋转树形并调整节点的颜色。

为方便讨论,我们称新节点为x,父节点为p,祖父节点为g,伯父节点为s,曾祖父节点为gg。现在,根据二叉搜索树的规则,新节点必为叶节点;根据红黑树规则4,x必为红,若p也是红色,则g必为黑。于是,根据x的插入位置与s、gg的颜色,有了以下5种情况:

情况1:x为根节点

直接把该节点设置为黑色。

情况2:p为黑色

直接插入,什么也不需要做

 

以下都是p为红色的情况

情况3:s为黑且x为左孩子(插入值为3的节点)

插入x后,x和p都是红色,违背了红黑树的特性4,将p变为黑色后,满足特性4;此时又违反特性5,即不同路径下的黑色节点数目变得不同,所以将g设为红色,然后做一次右旋来解决。

解决:先对g做一次右旋再更改p、g颜色,结果如下图

此时可能产生不平衡状态(高度相差1以上),但这并不会影响红黑树的搜寻效率。经验告诉我们,红黑树的搜寻效率几乎和AVL-tree相等。

情况4:s为黑且x为右孩子(插入值为8的节点)

解决:首先对p进行一次左旋,并改改gx的颜色,然后对g进行一次右旋。如下图

情况5:s为红(插入值为75的节点)

先进行一次右旋,并改变x的颜色,如果此时gg为黑则一切结束,如果gg为红见情况6。

 

情况6:s为红且x为外侧插入(插入值为35的节点)

对节点g做一次右旋,改变x的颜色,如果gg为红,则继续向上,直到不再有父子连续为红的情况。

为了避免情况4“父子节点皆为红”的情况持续向上发生,造成性能上的损耗,会施行一个 top-down procedure。

假设新增节点为a,那么就沿着a的路径,只要看到存在一个节点的两个子节点都是红色,就把该节点改为红色,然后把两个子节点改为黑色。

但如果该节点的父节点也是红色,就需要像情况1或2一样进行调整(此时该节点可以看作x节点,如上图,对值为70的节点进行右旋并改变它和值为60的节点的颜色)。

因此,插入值为35的节点的操作就很单纯了,要么直接插入,要么插入后像情况1或2一样进行调整。

 

下图是 SGI STL 的红黑树结构与其节点构成

红黑树提供遍历操作及iterator,按正常规则(ite++)遍历,就能获得排序状态。

我们不应该使用红黑树的iterator改变元素值,因为元素本身有其排列规则。但在编程层面并没有禁止这件事,这是因为红黑树会为set和map提供服务,而map允许其data部分改变,只有key部分不能改变。

红黑树中提供两种插入操作,分别是insert_unique()和insert_equal(),前者表示节点的key值一定在红黑树中独一无二,否则插入失败;后者表示key值可以重复。

红黑树的数据结构

可以看到,其中有专门的空间配置器;keyOfValue是一个仿函数(一个类中重写operator() ),用来取出value中的键值key,Compare用来表现节点的大小比较方式;value表示key + data。

红黑树的迭代器

迭代器是将一种结构实现为泛型容器的关键。我们需要考虑它的类别、前进、后退、成员访问等操作。

红黑树的节点与迭代器之间的关系如下图

红黑树的迭代器属于双向迭代器,但不具备随机定位的能力。迭代器的++ 与 -- 操作都是调用基层迭代器的increment()与decrement() 操作。前进或后退完全依据二叉搜索树的节点排列法则(root和header是SGI STL的一种特殊实现技巧)。

RB_tree 的构造与内存管理

RB_tree的构造方式有两种,一种是clone一个已经存在的RB_tree,另一种是构造一颗新的树。如下列语句:

rb_tree<int,int,identity<int>,less<int>,alloc> itree;

它会调用RB_tree的默认构造函数,默认构造函数会调用init()函数。

void init(){

header = get_node();//header指向一块节点空间
color(header)  = __rb_tree_red;//令header为红色(为了和root)进行区分

root() = 0;
leftmost = header;//令header的左子节点为自己
rightmost = header;//令header的右子节点为自己

}

header和root的关系如下图,它们互为对方的父节点。

RB_tree的元素操作

红黑树中提供两种插入操作,分别是insert_unique()和insert_equal(),前者表示节点的key值一定在红黑树中独一无二,否则插入失败;后者表示key值可以重复,除非空间不足导致插入失败。

// sgi stl _Rb_tree 插入算法 insert_equal() 实现.
// 策略概述: insert_equal() 在红黑树找到自己的位置,
// 然后交由 _M_insert() 来处理接下来的工作.
// _M_insert() 会将节点插入红黑树中, 接着调整红黑树,
// 维持性质.
template <class _Key, class _Value, class _KeyOfValue,
          class _Compare, class _Alloc>
typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>
  ::insert_equal(const _Value& __v)
{
  // 在红黑树中有头结点和根节点的概念, 头结点位于根节点之上,
  // 头结点只为管理而存在, 根节点是真正存储数据的地方. 头结点和根节点互为父节点,
   // 是一种实现的技巧.
  _Link_type __y = _M_header; // 指向头结点
  _Link_type __x = _M_root(); // _M_header->_M_parent, 即指向根节点
 
  // 寻找插入的位置
  while (__x != 0) {
    __y = __x;
 
    // 小于当前节点要走左边, 大于等于当前节点走右边
    __x = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)) ?
            _S_left(__x) : _S_right(__x);
  }
  // __x 为需要插入的节点的位置, __y 为其父节点
  return _M_insert(__x, __y, __v);
}
 
// sgi stl _Rb_tree 插入算法 insert_unique() 实现.
// 策略概述: insert_unique() 同样也在红黑树中找到自己的位置; 我们知道,
// 如果小于等于当前节点会往右走, 所以遇到一个相同键值的节点后, 会往右走一步,
// 接下来一直往左走, 所以下面的实现会对往左走的情况做特殊的处理.
template <class _Key, class _Value, class _KeyOfValue,
          class _Compare, class _Alloc>
pair<typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator,
     bool>
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>
  ::insert_unique(const _Value& __v)
{
  _Link_type __y = _M_header; // 指向头结点
  _Link_type __x = _M_root(); // 指向根节点, 可能为空
  bool __comp = true;
 
  // 寻找插入的位置
  while (__x != 0) {
    __y = __x;
    __comp = _M_key_compare(_KeyOfValue()(__v), _S_key(__x));
 
    // 小于当前节点要走左边, 大于等于当前节点走右边
    __x = __comp ? _S_left(__x) : _S_right(__x);
  }
 
  iterator __j = iterator(__y); // 在 __y 上建立迭代器
 
  // 我认为下面判断树中是否有存在键值的情况有点绕,
  // 它充分利用了二叉搜索树的性质, 如此做很 hack, 但不易理解.
  // 要特别注意往左边插入的情况.
 
  // HACKS:
  // 下面的 if 语句是比 __x 小走左边的情况: 会发现, 如果插入一个已存在的键的话,
  // __y 最终会定位到已存在键的右子树的最左子树.
  // 譬如, 红黑树中已经存在一个键为 100 的节点, 其右孩子节点为 101,
  // 此时如果再插入键为 100 的节点, 因为 100<=100, 所以会往右走到达 101 节点,
  // 有 100<101, 继而往左走, 会一直往左走.大家稍微画一个例子就能理解.
  if (__comp)
    // 特殊情况, 如果 __j 指向了最左孩子, 那么肯定要插入新节点.
    if (__j == begin())
      return pair<iterator,bool>(_M_insert(__x, __y, __v), true);
    // 其他情况, 这个时候也是往左边插入, 如果存在重复的键值,
    // 那么 --__j 能定位到此重复的键的节点.
    else
      --__j;
 
  // HACKS: 这里比较的是 __j 和 __v, 如果存在键值, 那么 __j == __v,
  // 会跳过 if 语句. 否则执行插入. 也就是说如果存在重复的键, 那么 __j
  // 的值肯定是等于 __v
  if (_M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)))
    return pair<iterator,bool>(_M_insert(__x, __y, __v), true);
 
  // 此时 __y.value = __v, 不允许插入, 返回键值所在位置
  return pair<iterator,bool>(__j, false);
}
 
// _M_insert() 是真正执行插入的地方.
// 策略概述: 插入策略已经在上篇中详述, 可以根据上篇文章的描述,
// 和下面代码的注释, 加深对红黑树插入算法里理解
template <class _Key, class _Value, class _KeyOfValue,
          class _Compare, class _Alloc>
typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>
  ::_M_insert(_Base_ptr __x_, _Base_ptr __y_, const _Value& __v)
{
  _Link_type __x = (_Link_type) __x_; // 新节点插入的位置.
  // 关于 __x 的疑问:
  // 1. 它被放到下面的, 第一个 if 语句中, 我觉得是没有必要的,
  // 因为从调用 _M_insert() 的函数来看, __x 总是为空.
  // 2. 既然 __x 是新节点插入的位置, 那么为什么不直接在 __x 上创建节点,
  // 还要在下面通过比较来决定新节点是左孩子还是右孩子;
  // 不如直接用指针的指针或者指针的引用来完成, 省去了下面的判断.
 
  _Link_type __y = (_Link_type) __y_; // 新节点的父节点
  _Link_type __z; // 新节点的位置
 
  if (__y == _M_header || __x != 0 ||
      _M_key_compare(_KeyOfValue()(__v), _S_key(__y))) {
  // 新节点应该为左孩子
    __z = _M_create_node(__v);
    _S_left(__y) = __z;               // also makes _M_leftmost() = __z
                                      //    when __y == _M_header
    if (__y == _M_header) {
      _M_root() = __z;
      _M_rightmost() = __z;
    }
    else if (__y == _M_leftmost())
      _M_leftmost() = __z;   // maintain _M_leftmost() pointing to min node
  }
  // 新节点应该为右孩子
  else {
    __z = _M_create_node(__v);
    _S_right(__y) = __z;
    if (__y == _M_rightmost())
      _M_rightmost() = __z;  // maintain _M_rightmost() pointing to max node
  }
  _S_parent(__z) = __y;
  _S_left(__z) = 0;
  _S_right(__z) = 0;
 
  // 重新调整
  _Rb_tree_rebalance(__z, _M_header->_M_parent);
 
  // 更新红黑树节点数
  ++_M_node_count;
 
  // 返回迭代器类型
  return iterator(__z);
}
 
// 插入新节点后, 可能会破坏红黑树性质, _Rb_tree_rebalance() 负责维持性质.
// 其中:
// __x 新插入的节点
// __root 根节点
// 策略概述: 红黑树插入重新调整的策略已经在上篇中讲述,
// 可以结合上篇文章和这里的代码注释,
// 理解红黑树的插入算法.
inline void
_Rb_tree_rebalance(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{
  // 将新插入的节点染成红色
  __x->_M_color = _S_rb_tree_red;
 
  while (__x != __root && __x->_M_parent->_M_color == _S_rb_tree_red) {
    // __x 的父节点也是红色的情况. 提示: 如果是黑色节点, 不会破坏红黑树性质.
 
    if (__x->_M_parent == __x->_M_parent->_M_parent->_M_left) {
      // 叔父节点
      _Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_right;
 
      if (__y && __y->_M_color == _S_rb_tree_red) {
        // 第 1 种情况, N,P,U 都红(G 肯定黑).
        // 策略: G->红, N,P->黑. 此时, G 红, 如果 G 的父亲也是红, 性质又被破坏了,
        // HACK: 可以将 GPUN 看成一个新的红色 N 节点, 如此递归调整下去;
        // 特俗的, 如果碰巧将根节点染成了红色, 可以在算法的最后强制 root->红.
        __x->_M_parent->_M_color = _S_rb_tree_black;
        __y->_M_color = _S_rb_tree_black;
        __x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
        __x = __x->_M_parent->_M_parent;
      }
      else {
 
        if (__x == __x->_M_parent->_M_right) {
        // 第 2 种情况, P 为红, N 为 P 右孩子, U 为黑或缺少.
        // 策略: 旋转变换, 从而进入下一种情况:
          __x = __x->_M_parent;
          _Rb_tree_rotate_left(__x, __root);
        }
        // 第 3 种情况, 可能由第二种变化而来, 但不是一定: P 为红, N 为红.
        // 策略: 旋转, 交换 P,G 颜色, 调整后, 因为 P 为黑色, 所以不怕
        // P 的父节点是红色的情况. over
        __x->_M_parent->_M_color = _S_rb_tree_black;
        __x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
        _Rb_tree_rotate_right(__x->_M_parent->_M_parent, __root);
      }
    }
    else { // 下面的代码是镜像得出的, 脑补吧.
      _Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_left;
      if (__y && __y->_M_color == _S_rb_tree_red) {
        __x->_M_parent->_M_color = _S_rb_tree_black;
        __y->_M_color = _S_rb_tree_black;
        __x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
        __x = __x->_M_parent->_M_parent;
      }
      else {
        if (__x == __x->_M_parent->_M_left) {
          __x = __x->_M_parent;
          _Rb_tree_rotate_right(__x, __root);
        }
        __x->_M_parent->_M_color = _S_rb_tree_black;
        __x->_M_parent->_M_parent->_M_color = _S_rb_tree_red;
        _Rb_tree_rotate_left(__x->_M_parent->_M_parent, __root);
      }
    }
  }
  __root->_M_color = _S_rb_tree_black;
}
// 删除算法, 直接调用底层的删除实现 _Rb_tree_rebalance_for_erase().
template <class _Key, class _Value, class _KeyOfValue,
          class _Compare, class _Alloc>
inline void _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>
  ::erase(iterator __position)
{
  _Link_type __y =
    (_Link_type) _Rb_tree_rebalance_for_erase(__position._M_node,
                                              _M_header->_M_parent,
                                              _M_header->_M_left,
                                              _M_header->_M_right);
  destroy_node(__y);
  --_M_node_count;
}
 
// 删除节点底层实现, 删除可能会破坏红黑树性质,
// _Rb_tree_rebalance()
// 负责维持性质. 其中:
// __z 需要删除的节点
// __root 根节点
// __leftmost 红黑树内部数据, 即最左子树
// __rightmost 红黑树内部数据, 即最右子树
// 策略概述: _Rb_tree_rebalance_for_erase() 会根据
// 删除节点的位置在红黑树中找到顶替删除节点的节点,
// 即无非是删除节点左子树的最大节点或右子树中的最小节点,
// 此处用的是有一种策略. 接着, 会调整红黑树以维持性质.
// 调整的算法已经在上篇文章中详述, 可以根据上篇文章的描述
// 和此篇的代码注释, 加深对红黑树删除算法的理解.
inline _Rb_tree_node_base*
_Rb_tree_rebalance_for_erase(_Rb_tree_node_base* __z,
                             _Rb_tree_node_base*& __root,
                             _Rb_tree_node_base*& __leftmost,
                             _Rb_tree_node_base*& __rightmost)
{
  // __z 是要删除的节点
 
  // __y 最终会指向要删除的节点
  _Rb_tree_node_base* __y = __z;
  // N 节点
  _Rb_tree_node_base* __x = 0;
  // 记录 N 节点的父节点
  _Rb_tree_node_base* __x_parent = 0;
 
  // 只有一个孩子或者没有孩子的情况
  if (__y->_M_left == 0)     // __z has at most one non-null child. y == z.
    __x = __y->_M_right;     // __x might be null.
  else
    if (__y->_M_right == 0)  // __z has exactly one non-null child. y == z.
      __x = __y->_M_left;    // __x is not null.
 
    // 有两个非空孩子
    else {                   // __z has two non-null children.  Set __y to
      __y = __y->_M_right;   //   __z's successor.  __x might be null.
 
      // __y 取右孩子中的最小节点, __x 记录他的右孩子(可能存在右孩子)
      while (__y->_M_left != 0)
        __y = __y->_M_left;
      __x = __y->_M_right;
    }
 
  // __y != __z 说明有两个非空孩子的情况,
  // 此时的删除策略就和文中提到的普通二叉搜索树删除策略一样:
  // __y 记录了 __z 右子树中最小的节点
  // __x 记录了 __y 的右孩子
  // 用 __y 顶替 __z 的位置, __x 顶替 __y 的位置, 最后用 __y 指向 __z,
  // 从而 __y 指向了要删除的节点
  if (__y != __z) {          // relink y in place of z.  y is z's successor
 
    // 将 __z 的记录转移至 __y 节点
    __z->_M_left->_M_parent = __y;
    __y->_M_left = __z->_M_left;
 
    // 如果 __y 不是 __z 的右孩子, __z->_M_right 有左孩子
    if (__y != __z->_M_right) {
 
      __x_parent = __y->_M_parent;
 
      // 如果 __y 有右孩子 __x, 必须有那个 __x 替换 __y 的位置
      if (__x)
        // 替换 __y 的位置
        __x->_M_parent = __y->_M_parent;
 
      __y->_M_parent->_M_left = __x;      // __y must be a child of _M_left
      __y->_M_right = __z->_M_right;
      __z->_M_right->_M_parent = __y;
    }
    // __y == __z->_M_right
    else
      __x_parent = __y;
 
    // 如果 __z 是根节点
    if (__root == __z)
      __root = __y;
 
    // __z 是左孩子
    else if (__z->_M_parent->_M_left == __z)
      __z->_M_parent->_M_left = __y;
 
    // __z 是右孩子
    else
      __z->_M_parent->_M_right = __y;
 
    __y->_M_parent = __z->_M_parent;
    // 交换需要删除节点 __z 和 替换节点 __y 的颜色
    __STD::swap(__y->_M_color, __z->_M_color);
    __y = __z;
    // __y now points to node to be actually deleted
  }
  // __y != __z 说明至多一个孩子
  else {                        // __y == __z
    __x_parent = __y->_M_parent;
    if (__x) __x->_M_parent = __y->_M_parent;
 
    // 将 __z 的父亲指向 __x
    if (__root == __z)
      __root = __x;
    else
      if (__z->_M_parent->_M_left == __z)
        __z->_M_parent->_M_left = __x;
      else
        __z->_M_parent->_M_right = __x;
 
    // __leftmost 和 __rightmost 是红黑树的内部数据, 因为 __z 可能是
    // __leftmost 或者 __rightmost, 因此需要更新.
    if (__leftmost == __z)
      if (__z->_M_right == 0)        // __z->_M_left must be null also
        // __z 左右孩子都为空, 没有孩子
        __leftmost = __z->_M_parent;
    // makes __leftmost == _M_header if __z == __root
      else
        __leftmost = _Rb_tree_node_base::_S_minimum(__x);
 
    if (__rightmost == __z)
      if (__z->_M_left == 0)         // __z->_M_right must be null also
        __rightmost = __z->_M_parent;
    // makes __rightmost == _M_header if __z == __root
      else                      // __x == __z->_M_left
        __rightmost = _Rb_tree_node_base::_S_maximum(__x);
 
    // __y 同样已经指向要删除的节点
  }
 
  // __y 指向要删除的节点
  // __x 即为 N 节点
  // __x_parent 指向 __x 的父亲, 即 N 节点的父亲
  if (__y->_M_color != _S_rb_tree_red) {
    // __y 的颜色为黑色的时候, 会破坏红黑树性质
 
    while (__x != __root && (__x == 0 || __x->_M_color == _S_rb_tree_black))
      // __x 不为红色, 即为空或者为黑. 提示: 如果 __x 是红色, 直接将 __x 替换成黑色
 
      if (__x == __x_parent->_M_left) { // 如果 __x 是左孩子
 
        _Rb_tree_node_base* __w = __x_parent->_M_right; // 兄弟节点
 
        if (__w->_M_color == _S_rb_tree_red) {
          //第 2 情况, S 红, 根据红黑树性质P,SL,SR 一定黑.
          // 策略: 旋转, 交换 P,S 颜色.
 
          __w->_M_color = _S_rb_tree_black;
          __x_parent->_M_color = _S_rb_tree_red; // 交换颜色
          _Rb_tree_rotate_left(__x_parent, __root); // 旋转
          __w = __x_parent->_M_right; // 调整关系
        }
 
        if ((__w->_M_left == 0 ||
             __w->_M_left->_M_color == _S_rb_tree_black) &&
            (__w->_M_right == 0 ||
             __w->_M_right->_M_color == _S_rb_tree_black)) {
          // 提示: 这是 第 1 情况和第 2.1 情况的合并, 因为处理的过程是一样的.
          // 但他们的情况还是要分门别类的. 已经在文章中详细支出,
          // 似乎大多数的博文中没有提到这一点.
 
          // 第 1 情况, N,P,S,SR,SL 都黑.
          // 策略: S->红. 通过 PN,PS 的黑色节点数量相同了, 但会比其他路径多一个,
          // 解决的方法是在 P 上从情况 0 开始继续调整.
          // 为什么要这样呢? HACKS: 因为既然 PN,PS
          // 路径上的黑节点数量相同而且比其他路径会少一个黑节点,
          // 那何不将其整体看成了一个 N 节点! 这是递归原理.
 
          // 第 2.1 情况, S,SL,SR 都黑.
          // 策略: P->黑. S->红, 因为通过 N 的路径多了一个黑节点,
          // 通过 S 的黑节点个数不变, 所以维持了性质 5. over
 
          // 可能大家会有疑问, 不对啊, 2.1 的情况,
          // 策略是交换父节点和兄弟节点的颜色, 此时怎么没有对父节点的颜色赋值呢?
          // HACKS: 这就是合并情况的好处, 因为就算此时父节点是红色,
          // 而且也将兄弟节点颜色改为红色, 你也可以将 PS,PN 看成一个红色的 N 节点,
          // 这样在下一个循环当中, 这个 N 节点也会变成黑色. 因为此函数最后有一句话:
          // if (__x) __x->_M_color = _S_rb_tree_black;
          // 合并情况, 节省代码量
 
          // 当然是可以分开写的
 
          // 兄弟节点染成黑色
          __w->_M_color = _S_rb_tree_red;
 
          // 调整关系
          __x = __x_parent;
          __x_parent = __x_parent->_M_parent;
        } else {
          if (__w->_M_right == 0 ||
              __w->_M_right->_M_color == _S_rb_tree_black) {
            // 第 2.2.1 情况, S,SR 黑, SL 红.
            // 策略: 旋转, 变换 SL,S 颜色.
 
            if (__w->_M_left) __w->_M_left->_M_color = _S_rb_tree_black;
            __w->_M_color = _S_rb_tree_red;
            _Rb_tree_rotate_right(__w, __root);
 
            // 调整关系
            __w = __x_parent->_M_right;
          }
 
          // 第 2.2.2 情况, S 黑, SR 红.
          // 策略: 旋转, 交换 S,P 颜色, SR->黑色, 重新获得平衡.
          __w->_M_color = __x_parent->_M_color;
          __x_parent->_M_color = _S_rb_tree_black;
          if (__w->_M_right) __w->_M_right->_M_color = _S_rb_tree_black;
          _Rb_tree_rotate_left(__x_parent, __root);
          break;
        }                        // 下面的代码是镜像得出的, 脑补吧.
      } else {                  // same as above, with _M_right <-> _M_left.
        _Rb_tree_node_base* __w = __x_parent->_M_left;
        if (__w->_M_color == _S_rb_tree_red) {
          __w->_M_color = _S_rb_tree_black;
          __x_parent->_M_color = _S_rb_tree_red;
          _Rb_tree_rotate_right(__x_parent, __root);
          __w = __x_parent->_M_left;
        }
        if ((__w->_M_right == 0 ||
             __w->_M_right->_M_color == _S_rb_tree_black) &&
            (__w->_M_left == 0 ||
             __w->_M_left->_M_color == _S_rb_tree_black)) {
          __w->_M_color = _S_rb_tree_red;
          __x = __x_parent;
          __x_parent = __x_parent->_M_parent;
        } else {
          if (__w->_M_left == 0 ||
              __w->_M_left->_M_color == _S_rb_tree_black) {
            if (__w->_M_right) __w->_M_right->_M_color = _S_rb_tree_black;
            __w->_M_color = _S_rb_tree_red;
            _Rb_tree_rotate_left(__w, __root);
            __w = __x_parent->_M_left;
          }
          __w->_M_color = __x_parent->_M_color;
          __x_parent->_M_color = _S_rb_tree_black;
          if (__w->_M_left) __w->_M_left->_M_color = _S_rb_tree_black;
          _Rb_tree_rotate_right(__x_parent, __root);
          break;
        }
      }
    if (__x) __x->_M_color = _S_rb_tree_black;
  }
  return __y;
}

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值