[小记]续红黑树插入和删除操作--SGI STL code注释

下面节选自STL源码剖析,简体是我的注释


rb tree的自增和自减内部函数,自增或者自减寻找的是中序后继和中序前继

void increment()
  {
    if (node->right != 0) {		// 如果有右子節點。狀況(1)
      node = node->right;		// 就向右走
      while (node->left != 0)	// 然後一直往左子樹走到底
        node = node->left;		// 即是解答
    }
    else {					// 沒有右子節點。狀況(2)
      base_ptr y = node->parent;	// 找出父節點,上溯
      while (node == y->right) {	// 如果現行節點本身是個右子節點,
        node = y;				// 就一直上溯,直到「不為右子節點」止。我的理解:如果上溯是右枝,表明是往前找,如果是左枝,表示往后
        y = y->parent;
      }
      if (node->right != y)		// 「若此時的右子節點不等於此時的父節點」。
        node = y;				// 狀況(3) 此時的父節點即為解答。
                                      // 否則此時的node 為解答。狀況(4)
    }						
    // 注意,以上判斷「若此時的右子節點不等於此時的父節點」,是為了應付一種
    // 特殊情況:我們欲尋找根節點的下一節點,而恰巧根節點無右子節點。
    // 當然,以上特殊作法必須配合 RB-tree 根節點與特殊之header 之間的
    // 特殊關係。
  }

  // 以下其實可實作於 operator-- 內,因為再無他處會呼叫此函式了。
  void decrement()
  {
    if (node->color == __rb_tree_red &&	// 如果是紅節點,且
        node->parent->parent == node)		// 父節點的父節點等於自己,
      node = node->right;				// 狀況(1) 右子節點即為解答。
    // 以上情況發生於node為header時(亦即 node 為 end() 時)。
    // 注意,header 之右子節點即 mostright,指向整棵樹的 max 節點。
    else if (node->left != 0) {			// 如果有左子節點。狀況(2)
      base_ptr y = node->left;			// 令y指向左子節點
      while (y->right != 0)				// 當y有右子節點時
        y = y->right;					// 一直往右子節點走到底
      node = y;						// 最後即為答案
    }
    else {							// 既非根節點,亦無左子節點。
      base_ptr y = node->parent;			// 狀況(3) 找出父節點
      while (node == y->left) {			// 當現行節點身為左子節點
        node = y;						// 一直交替往上走,直到現行節點
        y = y->parent;					// 不為左子節點,理解:一直找到不为左枝的父节点,即为前继节点
      }
      node = y;						// 此時之父節點即為答案
    }
  }


左旋和右旋操作,以传入第一个参数x为旋转点, 左旋和右旋分别对其右孩子和左孩子进行父子旋转

// 新節點必為紅節點。如果安插處之父節點亦為紅節點,就違反紅黑樹規則,此時必須
// 做樹形旋轉(及顏色改變,在程式它處)。
inline void 
__rb_tree_rotate_left(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  // x 為旋轉點
  __rb_tree_node_base* y = x->right;	// 令y 為旋轉點的右子節點
  x->right = y->left;
  if (y->left !=0)
    y->left->parent = x;		// 別忘了回馬槍設定父節點
  y->parent = x->parent;

  // 令 y 完全頂替 x 的地位(必須將 x 對其父節點的關係完全接收過來)
  if (x == root)					// x 為根節點
    root = y;
  else if (x == x->parent->left)	// x 為其父節點的左子節點
    x->parent->left = y;
  else							// x 為其父節點的右子節點
    x->parent->right = y;			
  y->left = x;
  x->parent = y;
}

// 新節點必為紅節點。如果安插處之父節點亦為紅節點,就違反紅黑樹規則,此時必須
// 做樹形旋轉(及顏色改變,在程式它處)。
inline void 
__rb_tree_rotate_right(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  // x 為旋轉點
  __rb_tree_node_base* y = x->left;	// y 為旋轉點的左子節點
  x->left = y->right;
  if (y->right != 0)
    y->right->parent = x; 	// 別忘了回馬槍設定父節點
  y->parent = x->parent;

  // 令 y 完全頂替 x 的地位(必須將 x 對其父節點的關係完全接收過來)
  if (x == root)					// x 為根節點
    root = y;
  else if (x == x->parent->right)	// x 為其父節點的右子節點
    x->parent->right = y;
  else							// x 為其父節點的左子節點
    x->parent->left = y;
  y->right = x;
  x->parent = y;
}

红黑树的插入操作,允许重复

template <class Key, class Value, class KeyOfValue, class Compare, class Alloc>   //一大堆的模板参数
typename rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::iterator //返回rb_tree的迭代器
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::
__insert(base_ptr x_, base_ptr y_, const Value& v) { //x为新位置,y是安插位置父节点,v是模板参数值,是实际内容
// 參數x_ 為新值安插點,參數y_ 為安插點之父節點,參數v 為新值。
  link_type x = (link_type) x_;
  link_type y = (link_type) y_;
  link_type z;

  // key_compare 是鍵值大小比較準則。應該會是個 function object。
  if (y == header || x != 0 || key_compare(KeyOfValue()(v), key(y))) { //【如果父节点为header,表明是空树;x!=0 未知;key_compare,v比y小则true;】
    z = create_node(v);  // 產生一個新節點
    left(y) = z;          // 這使得當 y 即為 header時,leftmost() = z //【z放置于y的左子树,因为比y小】
    if (y == header) { //【空树处理】
      root() = z;
      rightmost() = z;
    }
    else if (y == leftmost())	// 如果y為最左節點,更新最左域
      leftmost() = z;           	// 維護leftmost(),使它永遠指向最左節點
  }
  else {
    z = create_node(v);		// 產生一個新節點
    right(y) = z;				// 令新節點成為安插點之父節點 y 的右子節點
    if (y == rightmost())
      rightmost() = z;          	// 維護rightmost(),使它永遠指向最右節點
  }
  parent(z) = y;		// 設定新節點的父節點
  left(z) = 0;		// 設定新節點的左子節點
  right(z) = 0; 		// 設定新節點的右子節點
                          // 新節點的顏色將在 __rb_tree_rebalance() 設定(並調整)
  __rb_tree_rebalance(z, header->parent);	// 參數一為新增節點,參數二為 root (【最复杂的部分,调整红黑树,使得满足红黑树性质】)
  ++node_count;		// 節點數累加
  return iterator(z);	// 傳回一個迭代器,指向新增節點
}


红黑树插入balance调整 (父节点为祖父节点的左右子树各3种cases)



// 重新令樹形平衡(改變顏色及旋轉樹形)
// 參數一為新增節點,參數二為 root
inline void 
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  x->color = __rb_tree_red;		// 新節點必為紅,新插入节点全设置为红色
  while (x != root && x->parent->color == __rb_tree_red) { // 父節點為紅,如果父节点为黑色或者新插入节点为根(只需将新插入的根变色为黑色),则满足红黑性质
    if (x->parent == x->parent->parent->left) { // 父節點為祖父節點之左子節點, 【处理父节点是祖父节点的左子树的情况】
      __rb_tree_node_base* y = x->parent->parent->right;	// 令y 為伯父節點
      if (y && y->color == __rb_tree_red) { 		// 伯父節點存在,且為紅  【 Case 1, 叔父节点同为红色】
        x->parent->color = __rb_tree_black;  		// 更改父節點為黑
        y->color = __rb_tree_black;				// 更改伯父節點為黑
        x->parent->parent->color = __rb_tree_red; 	// 更改祖父節點為紅
        x = x->parent->parent;                  //【转向祖父节点,继续进行红黑调整】
      }
      else {	// 無伯父節點,或伯父節點為黑
        if (x == x->parent->right) { // 如果新節點為父節點之右子節點         【Case 2,插入节点是父节点的右子树,调整为左子树情况】
          x = x->parent;
          __rb_tree_rotate_left(x, root); // 第一參數為左旋點
        }
        x->parent->color = __rb_tree_black;	// 改變顏色                  【Case 3, 插入节点为父节点左子树情况,父节点变黑,祖父节点变红,右旋】
        x->parent->parent->color = __rb_tree_red;
        __rb_tree_rotate_right(x->parent->parent, root); // 第一參數為右旋點,【注意是以祖父节点为轴右旋】
      }
    }
    else {	// 父節點為祖父節點之右子節點                【处理父节点是祖父节点的右子树的情况】

      __rb_tree_node_base* y = x->parent->parent->left; // 令y 為伯父節點
      if (y && y->color == __rb_tree_red) {		// 有伯父節點,且為紅          【 Case 1】
        x->parent->color = __rb_tree_black;		// 更改父節點為黑
        y->color = __rb_tree_black; 				// 更改伯父節點為黑
        x->parent->parent->color = __rb_tree_red; 	// 更改祖父節點為紅
        x = x->parent->parent;	// 準備繼續往上層檢查...
      }
      else {	// 無伯父節點,或伯父節點為黑 
        if (x == x->parent->left) {	// 如果新節點為父節點之左子節點                 【Case 2, 节点,父节点和祖父节点不在一条线上】
          x = x->parent;
          __rb_tree_rotate_right(x, root); 	// 第一參數為右旋點
        }
        x->parent->color = __rb_tree_black;	// 改變顏色                            【 Case 3, 一条直线上的情况,父节点变黑,祖父变红,祖父节点左旋】
        x->parent->parent->color = __rb_tree_red;
        __rb_tree_rotate_left(x->parent->parent, root); // 第一參數為左旋點
      }
    }
  }	// while 結束
  root->color = __rb_tree_black;	// 根節點永遠為黑        【 最后别忘了置根颜色为黑色,因为前面没有处理插入点为根的情况】
}

红黑树的删除

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.node,       //【直接调用delete re-balance,因为不用寻找删除点】
                                                          header->parent,
                                                          header->left,
                                                          header->right);
  destroy_node(y);         //【销毁节点】
  --node_count;           //【节点数更新】
}


红黑树删除的balance操作,非常复杂(左右子树各4种主要cases,外加一点额外的其他情况)


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)  //最右点,同上
{
  __rb_tree_node_base* y = z;
  __rb_tree_node_base* x = 0;  //用x表示后续节点,来代替删除点
  __rb_tree_node_base* x_parent = 0;
  if (y->left == 0)             // // 如果删除点没有左子树
    x = y->right;               // 那么选右子树来替换,注意,对于没有左右子树的节点来说,x可能为空,不过无所谓
  else
    if (y->right == 0)          // 如果左子树不空,但是右子树为空,则选右子树为替代者
      x = y->left;              // x is not null.
    else {                      // 如果左右子树都不空,那么需要寻找删除节点的中序后继节点来替换
      y = y->right;             // 也就是找右子树的“最左下节点”.
      while (y->left != 0)  //一直往左找
        y = y->left;         //y为右子树上的最小值,注意这个节点,必然【只有一个右孩子,或者没有右孩子】,因为他是右子树上的最小值!!!
      x = y->right;         //找到后,x即为删除点的“后”节点,将用来替换y节点
    }
  if (y != z) {                 //如果y!=z表示,后继节点情况,直接
    z->left->parent = y;      //将z的左子树变成y的左子树
    y->left = z->left;
    if (y != z->right) {   //如果y不是z的右孩子,【注意,整个过程实际上是将节点z用y替换的过程(一般直接用值copy就可以了,呵呵)】
      x_parent = y->parent;   //暂取y的父节点
      if (x) x->parent = y->parent;  //如果x不为空,即,y的右子树不为空,则x的父节点转成y的父节点
      y->parent->left = x;      // 将x挂到y的父节点的左子树上
      y->right = z->right;      //z的右子树成为y的右子树
      z->right->parent = y;    
    }
    else
      x_parent = y;  
    if (root == z)             //如果删除点是根节点,则root变成y ,y是z的中序后继节点
      root = y;
    else if (z->parent->left == z) //否则如果z为左枝,则将y挂到左枝
      z->parent->left = y;
    else 
      z->parent->right = y;        //z为右枝,则y挂到右枝
    y->parent = z->parent;         //变换y的父节点为z的父节点
    __STD::swap(y->color, z->color);   //替换y和z的颜色!!!
    y = z;                      //现在指向即将被删除的z节点
    // y now points to node to be actually deleted
  }
  else {                        // y == z  // y == z 【以下是对y不是后继节点的情况】
    x_parent = y->parent;
    if (x) x->parent = y->parent;   
    if (root == z)           //z为根,则子节点x为新的根
      root = x;
    else 
      if (z->parent->left == z)  //否则孩子节点x将对应删除节点的左枝或者右枝
        z->parent->left = x;
      else
        z->parent->right = x;
    if (leftmost == z) 
      if (z->right == 0)        // z->left must be null also
        leftmost = z->parent;  //如果z是最小,最左节点,则更新最左节点为z的父节点,因为z的父节点恰好比z大一点,是z的中序后继
    // makes leftmost == header if z == root
      else
        leftmost = __rb_tree_node_base::minimum(x);   //否则计算x为根的最左
    if (rightmost == z)  
      if (z->left == 0)         // z->right must be null also
        rightmost = z->parent;  
    // makes rightmost == header if z == root
      else                      // x == z->left
        rightmost = __rb_tree_node_base::maximum(x);  //最右更新同理
  }                  //【以下是真正复杂的删除后调整】
  if (y->color != __rb_tree_red) { //仅仅如果删除的节点为黑色,才会触发调整动作
    while (x != root && (x == 0 || x->color == __rb_tree_black)) //如果x是根,或者x的颜色为黑色,或者x为空,则结束调整
      if (x == x_parent->left) { //【删除节点为父节点的左子树情况讨论】
        __rb_tree_node_base* w = x_parent->right; //兄弟节点w
        if (w->color == __rb_tree_red) {      //  【case 1,兄弟节点为红色】
          w->color = __rb_tree_black;   //调整兄弟节点为黑色,父节点为红色,以父节点为轴进行左旋,调整为其他case
          x_parent->color = __rb_tree_red;
          __rb_tree_rotate_left(x_parent, root);
          w = x_parent->right;   //更新旋转之后的兄弟节点
        }
        if ((w->left == 0 || w->left->color == __rb_tree_black) &&
            (w->right == 0 || w->right->color == __rb_tree_black)) {//【case 2,如果兄弟节点左右子树为全黑色,可以认为空表示黑色】
          w->color = __rb_tree_red;  //变换非空兄弟的颜色为红色,转而以父节点为x调整父节点红黑特性
          x = x_parent;
          x_parent = x_parent->parent;
        } else {   //【case 3, 如果兄弟节点右孩子为黑色,但是左孩子为红色】
          if (w->right == 0 || w->right->color == __rb_tree_black) {
            if (w->left) w->left->color = __rb_tree_black;  //调整左孩子颜色为黑色
            w->color = __rb_tree_red;     //调整兄弟节点为红色
            __rb_tree_rotate_right(w, root);   //以兄弟节点为轴,右旋兄弟节点和左孩子,转换case 3为case 4
            w = x_parent->right;
          } //【case 4, 如果兄弟节点右孩子为红色,不管左孩子颜色】
          w->color = x_parent->color;    //右孩子颜色变黑,兄弟节点变成原父节点的颜色,原父节点变黑
          x_parent->color = __rb_tree_black;
          if (w->right) w->right->color = __rb_tree_black;
          __rb_tree_rotate_left(x_parent, root);  //以父节点为轴,进行左旋
          break;   //case 4的调整完后,就可以满足红黑特性了,可以直接退出了
        }
      } else {                  // //如果删除节点为父节点的右孩子的情况,跟上面的左孩子情况对应.
        __rb_tree_node_base* w = x_parent->left;  //左兄弟节点w
        if (w->color == __rb_tree_red) {  //【case 1, 兄弟节点为红的情况,变父节点为红,变兄弟节为黑,父节点为轴左旋,转换为后面的几种case】
          w->color = __rb_tree_black;  
          x_parent->color = __rb_tree_red;
          __rb_tree_rotate_right(x_parent, root);
          w = x_parent->left;     //更新新的兄弟节点
        }
        if ((w->right == 0 || w->right->color == __rb_tree_black) &&
            (w->left == 0 || w->left->color == __rb_tree_black)) { //【case 2, 兄弟节点的左右子树均为黑,空认为是黑色】
          w->color = __rb_tree_red;   //兄弟节点变红
          x = x_parent;                 //上溯到父节点继续进行红黑平衡
          x_parent = x_parent->parent;
        } else {
          if (w->left == 0 || w->left->color == __rb_tree_black) {  //【case 3,兄弟节点w的左子树为黑,右子树为红】
            if (w->right) w->right->color = __rb_tree_black;  //变兄弟节点w的左子树为红,右子树为黑
            w->color = __rb_tree_red;
            __rb_tree_rotate_left(w, root);  //以兄弟节点为轴,进行左旋
            w = x_parent->left;           //更新新的兄弟节点,转换成case 4的情况
          }
          w->color = x_parent->color; //【case 4,兄弟节点w的左子树为红,右子树无所谓】变兄弟节点为原父节点颜色
          x_parent->color = __rb_tree_black;  //父节点变黑,兄弟节点w的红色左孩子变黑
          if (w->left) w->left->color = __rb_tree_black;
          __rb_tree_rotate_right(x_parent, root);   //以父节点为轴,进行右旋
          break;   //case 4变换之后,就可以满足红黑特性了,直接退出调整
        }
      }
    if (x) x->color = __rb_tree_black;  //最后调整x,如果x为红色节点,直接变黑就可以满足条件了,上面调整的都是x原本就是黑色的情况
  }
  return y;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值