STL RB tree

一、为什么需要红黑树

对于一个有序数列,为了得到对数时间的插入和访问的速度,提出了二叉搜索树,这个树的规则就是任何节点的键值一定大于其左子树中每一个节点的键值,并小于右子树中每一个节点的键值。
注意:要和最大堆区别开来,最大堆是父节点一定大于自己的子节点。相当于二叉搜索树是有从左往右变大的趋势,而最大堆则是有从下往上变大的趋势。

但是二叉搜索树很有可能产生一种问题,就是不平衡。不平衡没有一个严格的定义,可以大致理解一下,如下:
在这里插入图片描述
不平衡的树会导致插入或者查找的效率较低,所以二叉搜索树应该尽可能的平衡,那么如何平衡呢,最常用的是两种方法,或者说是两种树。一种是AVL tree,还有一种就是RB tree。
这两种方法都是在二叉搜索树的基础之上,增加了一些限定条件,如果不满足限定条件,就调整树的结构,直到满足限定条件为止。简单说一下AVL tree的限定条件很简单,就是任何节点的左右子树的高度最多相差1.而RB tree则复杂很多。
所以讲到这里就可以知道,RB tree是为了更高效的实现有序数列的存储,查找,插入。并且RB tree和AVL tree都是二叉搜索树,都满足二叉搜索树的条件。

二、红黑树基本知识

①红黑树的基本规则
  • 每个结点要么是红的要么是黑的。
  • 根结点是黑的。
  • 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。
  • 如果一个结点是红的,那么它的两个儿子都是黑的。
  • 对于任意结点而言,其到叶结点的每条路径都包含相同数目的黑结点。

注意:a.红黑树中的叶节点和普通树中的叶节点是两种概念。普通树中的叶节点就是没有子节点的节点,也就是树的最后一层,但是红黑树中的叶节点则是没有存放真实数据的一层。如图所示:
在这里插入图片描述
其实红黑树中的叶子节点在实际存储中是不存在的,只是在算法规则上会考虑进去。(在本文中,所说的叶子节点都是指红黑树中的叶子节点)
b.对第五条规则的解读:任意一个非叶子节点到所有叶子结点的路径中,黑节点的数目都是相同的。

②左旋和右旋

在红黑树插入过程中,插入了一个节点以后,可能会不满足红黑树的五条规则,那么红黑树需要调整,调整的方法只有两种,旋转和变换颜色。

  • 左旋(我认为比较生动形象的图)相当于把右子节点变为父节点,原父节点变为左子节点。理解为父节点左移,所以是左旋
    在这里插入图片描述
  • 右旋。相当于把左子节点变为父节点,原父节点变为右子节点。理解为父节点右移,所以是右旋
    在这里插入图片描述

三、STL中红黑树数据结构

红黑树中每一个节点的设计:
一个节点中有五个成员变量,源码如下:

struct _Rb_tree_node_base
{
  typedef _Rb_tree_Color_type _Color_type;
  typedef _Rb_tree_node_base* _Base_ptr;

  _Color_type _M_color; //节点颜色,是一个bool类型
  _Base_ptr _M_parent;//指向父节点
  _Base_ptr _M_left;//指向左子节点
  _Base_ptr _M_right;//指向右子节点
...
}

template <class _Value>
struct _Rb_tree_node : public _Rb_tree_node_base
{
  typedef _Rb_tree_node<_Value>* _Link_type;
  _Value _M_value_field;//节点值
};

在一棵红黑树中,主要依靠下面三个成员变量来管理整颗树的:

struct _Rb_tree_base
{
...
protected:
  _Rb_tree_node<_Tp>* _M_header;//头结点,这是stl额外给红黑树加入的一个节点,stl将header节点的父节点设为
  //root节点,并且将root父节点设为header节点,另外让header左子节点指向整棵树的最小值(也就是最左下方的元素),右子节点指向整棵树的最大值(也就是最右下方的元素)。
...
};

class _Rb_tree : protected _Rb_tree_base<_Value, _Alloc> {
protected:
...
  size_type _M_node_count; //树的节点数量
  _Compare _M_key_compare; //节点键值的比较准则
...
};

尤其注意其中的header节点,可以用下面这个示意图来理解:
在这里插入图片描述

四、STL中红黑树迭代器

红黑树中的迭代器属于双向迭代器Bidirectional iterator,拥有自增和自减运算。
迭代器中存储的只有一个数据,就是指向红黑树一个节点的指针:

	_Base_ptr _M_node;

然后重载了运算符++和–。重载的这两个运算符很有讲究。
++运算符中调用了下面这个函数:

void _M_increment()
{
  if (_M_node->_M_right != 0) {          //如果迭代器指向的节点有右子节点
    _M_node = _M_node->_M_right;         //就向右走一个节点
    while (_M_node->_M_left != 0)        //然后一直走左子树走到底
      _M_node = _M_node->_M_left;        //这个找到的就是整棵树中比_M_node值大的所有集合中最小的那个元素
  }
  else {                                 //如果迭代器指向的节点没有右子节点
    _Base_ptr __y = _M_node->_M_parent;  //找到父节点
    while (_M_node == __y->_M_right) {   //如果当前节点是右子节点,就循环往上找父节点,知道找到一个父节点不是右子节点为止
      _M_node = __y;                     
      __y = __y->_M_parent;              
    }
    if (_M_node->_M_right != __y)        //如果此时的右子节点不等于父节点
      _M_node = __y;                     //那么此时的父节点就是++得到的值,其实也是找到的就是整棵树中比_M_node值大的所有集合中最小的那个元素
      //但是有两种特殊情况,不会进入这个判断,也就是_M_node指向的节点值是整颗树中最大值,见下面具体解释
  }
}

特殊情况一:假设现在有这样一颗红黑树
在这里插入图片描述
现在有一个迭代器指向80那个节点。那么执行++的过程会执行到第二个循环中,然后在循环结束时,应该是这个状态:
在这里插入图片描述
__y->_M_right因为指向了最大元素,而不是root,所以跳出循环。又因为root->right !=header,所以红黑树中最大值++是header。但是好像header++,仍然又回到了最大值处,这样就成了死循环了。

特殊情况二:就如第三点中的图一样,如果根节点没有右子节点,此时迭代器指向根节点,那么根节点++,就不会经过最后那个判断,最后结果仍然是根节点。

–运算中调用了这个函数:

void _M_decrement()
{
  if (_M_node->_M_color == _S_rb_tree_red &&
      _M_node->_M_parent->_M_parent == _M_node)//如果是红节点并且祖父节点就是自己,这就是头结点
    _M_node = _M_node->_M_right;//结果直接为右子节点,也就是树中的最大值
  else if (_M_node->_M_left != 0) {//如果有左子节点
    _Base_ptr __y = _M_node->_M_left;//先往左子节点走一格,然后一直往右走,走到底就是结果
    while (__y->_M_right != 0)
      __y = __y->_M_right;
    _M_node = __y;
  }
  else {
    _Base_ptr __y = _M_node->_M_parent;//如果没有右子节点,一直往上找到一个节点不是自己父节点的左子节点
    while (_M_node == __y->_M_left) {
      _M_node = __y;
      __y = __y->_M_parent;
    }
    _M_node = __y;//父节点就是最后的结果
  }//其实后面两个判断,就是在找树中比迭代器指向的值还要小的值集合中的最大值
}

如果是整棵树中最小值–,那么结果就是头结点,然后头结点再–,就是=变成了整棵树的最大值。

五、迭代器和节点部分数据的继承关系

根据刚才的源码可以看到,迭代器中的指针指向的是节点数据的基类指针,具体的关系图如下:
在这里插入图片描述
其实在我看的stl版本中,基本所有的容器都是这样的,我认为这样做,结果比较清晰,不容易混淆,比如迭代器一般是不会对数据做改变的,这样迭代器如果不适用星号*或者强转指针,是无法查看或者改变节点值的。
另外可以看到无论是节点结构还是迭代器结构,都是使用的struct而不是class。其实在C++中,struct和class用法是一样的,唯一的不一样就是,struct中默认的成员访问类型是public,而class中默认的成员访问类型是private。

五、红黑树的插入元素操作

在stl中红黑树的插入有两个版本,insert_equal和insert_unique。其中insert_equal允许插入重复的键值,而insert_unique不允许插入重复的键值。
①insert_equal源码如下:

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();//从根节点开始查找
  //找到合适的插入位置
  while (__x != 0) {
    __y = __x;
    __x = _M_key_compare(_KeyOfValue()(__v), _S_key(__x)) ? 
            _S_left(__x) : _S_right(__x);//如果v的键值大于等于x,x就往右下的子节点走,反之就往左下的子节点走。
  }
  return _M_insert(__x, __y, __v);
  //其中x为新值的插入点,y为插入点的父节点,v为新值
}

注意:本节中所有源码中,_M_key_compare函数可以传入两个参数,其中如果第一个参数小于第二个参数,就返回真,第一个参数大于等于第二个参数就返回假。
②insert_unique源码如下;

template <class _Key, class _Value, class _KeyOfValue, 
          class _Compare, class _Alloc>
pair<typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator, 
     bool>//返回值是一个pair结构,第一个元素是红黑树的迭代器,第二个元素是是否插入插入成功
_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);
  }
  //x就是要插入的位置,y是要插入位置的父节点
  iterator __j = iterator(__y);   
  if (__comp)//如果__comp为真,那么插入的值v小与y父节点的值,所以应该插入到y的左子节点
    if (__j == begin())//如果插入点的父节点是最左边的节点,防止出现最小值再--的情况,之前说过,这种情况得到的结果是header,没有意义     
      return pair<iterator,bool>(_M_insert(__x, __y, __v), true);
    else//不是,就调整j,也就是找到比j小的节点集合中最大值
      --__j;
  //如果comp为假,那么插入的值v大于或者等于y父节点的值,所以应该插入到y的有子节点,但这种情况下有可能出现等于的情况,需要把这种情况剔除出去。
  //之前已经判断了v大于等于y,而j=y,并且如果下面这个判断成立,那么y小于v
  if (_M_key_compare(_S_key(__j._M_node), _KeyOfValue()(__v)))//判断1
    return pair<iterator,bool>(_M_insert(__x, __y, __v), true);
  return pair<iterator,bool>(__j, false);
}

二叉搜索树有这样一个性质:如果一个没有子节点的节点x(x假设是迭代器,和红黑树迭代器用法一致)的值是a,并且节点x是父节点的左子节点,那么a的值一定在_S_key(x- -)和_S_key(x->parent)之间,左闭右开。
如果一个没有子节点的节点x(x假设是迭代器)的值是a,并且节点x是父节点的右子节点,那么a的值一定在_S_key(x->parent)和_S_key(x++)之间,左闭右开。
这个并没有证明,只是我自己总结出来的,有时间再仔细证明

如何判段值是否有重复,这里分为两种情况:
当__comp为真时,那么v一定是小于父节点的,属于第一种性质,所以唯一 可能相等的是(- -j)节点,所以在判断1中,进行两者的比较,如果为真,那说明_S_key(__j._M_node)小于v,那么就不存在等于的情况,如果为假,_S_key(__j._M_node)大于等于v,而根据性质1,_S_key(__j._M_node)小于等于v。那只有等于一种可能了。

当__comp为假时,那么v一定大于等于父节点,属于第二种性质,所以唯一可能相等的情况就是等于父节点,此时j就是y,如果判断1为假,那么在循环的最后一次得出了v大于等于y,而这里得出了y大于等于v,所以只有等于这种情况了。

注意:像找是否元素值重复,本来只要等于号判断一下就可以了,但是这里非得搞得很复杂,个人认为,主要是因为如果需要等于判断,就需要多一个模板参数,就是判断值相等的函数,这样比较麻烦,如果只需要一个模板参数comp,就可以实现所有功能,那么为什么还要多添加一个,而且只使用comp并没有带来时间上的损失。这个可以类比hashtable中,模板参数就需要多传入一个class EqualKey,而红黑树中如果需要是否相等,也需要这个模板参数。

③_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)
  //参数x为新值插入点,参数y为插入点的父节点,参数v为新值
{
  _Link_type __x = (_Link_type) __x_;
  _Link_type __y = (_Link_type) __y_;
  _Link_type __z;
  //下面一系列判断主要创建了节点空间,然后判断新节点是否是最左边的点或者是最右边的,如果是,对于header参数进行适当调整
  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 调整树结构的函数


inline void 
_Rb_tree_rebalance(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
//刚挂上的新节点为x,树的根节点为root
{
  __x->_M_color = _S_rb_tree_red;//刚挂上的新节点一定是红色
  while (__x != __root && __x->_M_parent->_M_color == _S_rb_tree_red) {//当父节点为红色
  //只有父节点为红色才需要调整,因为这样会出现父子双红的情况
    if (__x->_M_parent == __x->_M_parent->_M_parent->_M_left) {//父节点为祖父节点的左子节点时
      _Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_right;//y为伯父节点
      if (__y && __y->_M_color == _S_rb_tree_red) {//伯父节点存在且为红时,情况1
        __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;//x变为祖父节点
      }
      else {//无伯父节点,或者伯父节点为黑
        if (__x == __x->_M_parent->_M_right) {//如果新节点为父节点的右子节点,情况二
          __x = __x->_M_parent;
          _Rb_tree_rotate_left(__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_right(__x->_M_parent->_M_parent, __root);//以原先父节点改变后的的祖父节点为旋转点,进行右旋,在图中看更清楚
      }
    }
    else {//父节点为祖父节点的右子节点
      _Rb_tree_node_base* __y = __x->_M_parent->_M_parent->_M_left;//y为伯父节点
      if (__y && __y->_M_color == _S_rb_tree_red) {//有伯父节点,且为红色情况4,和情况1类似
        __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) {//如果新节点为父节点的左子节点,情况5,和情况2类似,只是先右旋,再左旋,但是旋转点都是一样的
          __x = __x->_M_parent;
          _Rb_tree_rotate_right(__x, __root);//x的父节点为旋转点
        }//新节点为父节点的右子节点,情况6,和情况3类似,只是进行左旋
        __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;
}

注意以下图中,白色节点表示这个节点的颜色暂时不重要
情况1:左边是调整前,右边是调整后,只是改变一下颜色
调整前调整后
情况2:原始情况
在这里插入图片描述
插入位置的父节点作为旋转点,进行一次左旋
在这里插入图片描述
再以最顶端的父节点作为旋转点,进行一次右旋,得到修改后的形状
在这里插入图片描述
情况3:原先结构
在这里插入图片描述
绕插入点的祖父节点左旋一次,得到修改后的节点
在这里插入图片描述
⑤_Rb_tree_rotate_left和_Rb_tree_rotate_right

//左旋函数,x节点是旋转点,也就是左旋中的父节点
inline void 
_Rb_tree_rotate_left(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{
  _Rb_tree_node_base* __y = __x->_M_right;//y是x的右子节点
  __x->_M_right = __y->_M_left;
  //将y的左子节点挂到x上
  if (__y->_M_left !=0)
    __y->_M_left->_M_parent = __x;
  __y->_M_parent = __x->_M_parent;
  //将y完全顶替x的位置,称为父节点
  if (__x == __root)
    __root = __y;
  else if (__x == __x->_M_parent->_M_left)
    __x->_M_parent->_M_left = __y;
  else
    __x->_M_parent->_M_right = __y;
  __y->_M_left = __x;
  __x->_M_parent = __y;
}

_Rb_tree_rotate_right

//右旋函数,x节点是旋转点,也就是右旋中的父节点
inline void 
_Rb_tree_rotate_right(_Rb_tree_node_base* __x, _Rb_tree_node_base*& __root)
{
  _Rb_tree_node_base* __y = __x->_M_left;//y为旋转点的左子节点
  __x->_M_left = __y->_M_right;
  //将y的右子节点挂到x上
  if (__y->_M_right != 0)
    __y->_M_right->_M_parent = __x;
  __y->_M_parent = __x->_M_parent;
  //令y完全顶替x的地位
  if (__x == __root)
    __root = __y;
  else if (__x == __x->_M_parent->_M_right)
    __x->_M_parent->_M_right = __y;
  else
    __x->_M_parent->_M_left = __y;
  __y->_M_right = __x;
  __x->_M_parent = __y;
}

六、stl红黑树中find函数

template <class _Key, class _Value, class _KeyOfValue, 
          class _Compare, class _Alloc>
typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::const_iterator 
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::find(const _Key& __k) const
{
  _Link_type __y = _M_header; /* Last node which is not less than __k. */
  _Link_type __x = _M_root(); /* Current node. */
  while (__x != 0) {
    if (!_M_key_compare(_S_key(__x), __k))
      __y = __x, __x = _S_left(__x);//y会记录最近一次_S_key(__x)≥__k的时候的x值
    else
      __x = _S_right(__x);
  }
  const_iterator __j = const_iterator(__y);   
  return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ?
    end() : __j;
    //因为之前j的值≥k的值,如果这里_M_key_compare(__k, _S_key(__j._M_node))为假,就是k的值≤j的值,所以k的值和j的值相等。所以会返回j。
}

七、stl红黑树中删除节点操作

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;
}

这是核心函数,但是我这里只是作了注释,因为在stl书上并没有详细解释,所以我也不确定我这里的注释正确性,有时间来进一步验证。

_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)
{
  _Rb_tree_node_base* __y = __z;
  _Rb_tree_node_base* __x = 0;
  _Rb_tree_node_base* __x_parent = 0;
  if (__y->_M_left == 0)     // __z has at most one non-null child. y == z.//z的左子节点为空
    __x = __y->_M_right;     // __x might be null.//x可能为空
  else
    if (__y->_M_right == 0)  // __z has exactly one non-null child. y == z.//z的左子节点不为空,右子节点为空
      __x = __y->_M_left;    // __x is not null.//x一定不为空
    else {                   // __z has two non-null children.  Set __y to//z的两个子节点都不为空
      __y = __y->_M_right;   //   __z's successor.  __x might be null.//寻找z的继任者
      while (__y->_M_left != 0)//找到左子节点为空的节点,y等于该节点,x等于该节点的右子节点
        __y = __y->_M_left;
      __x = __y->_M_right;
    }
  if (__y != __z) {          // relink y in place of z.  y is z's successor//用y继任z,这个y是没有左子节点的,只要z左右两个子节点都非空,就会进入这个判断
    __z->_M_left->_M_parent = __y; //z的左子节点挂到y上
    __y->_M_left = __z->_M_left;
    if (__y != __z->_M_right) {
      __x_parent = __y->_M_parent;//
      if (__x) __x->_M_parent = __y->_M_parent;
      __y->_M_parent->_M_left = __x;      // __y must be a child of _M_left//就是把y的原右子节点挂到y原父节点上,又因为y没有左子节点,所以y的原子节点都处理完了
      __y->_M_right = __z->_M_right;//z的右子节点挂到y上
      __z->_M_right->_M_parent = __y;
    }
    else//y是z的右子节点
      __x_parent = __y;  
    if (__root == __z)//z是根节点
      __root = __y;
    else if (__z->_M_parent->_M_left == __z)//将z的父节点指向y
      __z->_M_parent->_M_left = __y;
    else 
      __z->_M_parent->_M_right = __y;
    __y->_M_parent = __z->_M_parent;//变y的父节点为z的父节点
    __STD::swap(__y->_M_color, __z->_M_color);
    __y = __z;//将节点处理好以后,就让y指向实际被删除的节点
    // __y now points to node to be actually deleted
  }
  else {                        // __y == __z,z的两个子节点有一个为空,就会进入这个判断,个人认为就是一些特殊情况的处理,这里就不深究了
    __x_parent = __y->_M_parent;
    if (__x) __x->_M_parent = __y->_M_parent;  //我理解是用x来替换z 
    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;
    if (__leftmost == __z) 
      if (__z->_M_right == 0)        // __z->_M_left must be null also
        __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);
  }
  //总的来说,前面一步就是把红黑树上的节点给拿掉,并且这个拿掉也是有技巧的
  //接下来就是拿掉以后,对结构和颜色的调整
  if (__y->_M_color != _S_rb_tree_red) { //只有当y为黑是才需要调整
    while (__x != __root && (__x == 0 || __x->_M_color == _S_rb_tree_black))//x为黑色
      if (__x == __x_parent->_M_left) {
        _Rb_tree_node_base* __w = __x_parent->_M_right;
        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_left(__x_parent, __root);
          __w = __x_parent->_M_right;
        }
		//w为黑色的情况
        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)) {//w的左右孩子都为黑或者都为空的时候
          __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) {//w右孩子为黑,左孩子红时
            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;
          }
		  //下面为__w右孩子为红的情况
          __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 {// 当__x为右孩子时,与以上情况相同,只需对调左右即可.// 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值