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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章