1. 程式人生 > >STL原始碼分析之RB-tree關聯容器 上

STL原始碼分析之RB-tree關聯容器 上

前言

本節將分析STL中難度很高的RB-tree, 如果對紅黑樹有所認識的那麼分析起來的難度也就不是很大, 對紅黑樹沒有太多瞭解的直接來分析的難度就非常的大了, 可以對紅黑樹有個瞭解紅黑樹之原理和演算法詳細介紹. 紅黑樹是很類似與AVL-tree的, 但是因為AVL-tree在插入,刪除的操作做的實際操作很多, 綜合而言計算機對記憶體的管理都採用平衡性相對AVL-tree較差一點的紅黑樹.

RB-tree規則:

  1. 每個節點的顏色是黑色或者紅色
  2. 根節點必須是黑色的
  3. 每個葉節點(NULL)必須是黑色的
  4. 如果節點是紅色的, 則子節點必須是黑色的
  5. 從節點到每個子孫節點的路徑包括的黑色節點數目相同
// 紅黑定義
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false;	
const __rb_tree_color_type __rb_tree_black = true;

本節就只分析RB-tree的基本結構, 但rb-tree的很多功能都是呼叫基本結構實現的功能, 像左旋, 右旋, 刪除, 調整紅黑樹這些都非常的重要.

RB-tree基本結構分析

基本結構與list相似, 將節點與資料分開定義, __rb_tree_node_base定義指標; __rb_tree_node

繼承前者, 增加了資料, 就是一個完整的節點.

RB-tree基本結構

__rb_tree_node_base

struct __rb_tree_node_base
{
  typedef __rb_tree_color_type color_type;	
  typedef __rb_tree_node_base* base_ptr;	

  color_type color; 	// 定義節點顏色
  base_ptr parent;		// 定義父節點
  base_ptr left;		// 定義左孩子
  base_ptr right;		// 定義右孩子

    // 查詢最小節點
  static
base_ptr minimum(base_ptr x) { while (x->left != 0) x = x->left; return x; } // 查詢最大節點 static base_ptr maximum(base_ptr x) { while (x->right != 0) x = x->right; return x; } };

__rb_tree_node : 完整的節點

template <class Value>
struct __rb_tree_node : public __rb_tree_node_base	// 繼承__rb_tree_node_base
{
  typedef __rb_tree_node<Value>* link_type;
  Value value_field;	// 定義節點資料
};

RB-tree迭代器

__rb_tree_base_iterator 迭代器基本結構

迭代器中incrementdecrement函式是實現++與–的功能的核心.

struct __rb_tree_base_iterator
{
  typedef __rb_tree_node_base::base_ptr base_ptr;
  typedef bidirectional_iterator_tag iterator_category;	// bidirectional_iterator_tag型別的迭代器
  typedef ptrdiff_t difference_type;
  base_ptr node;	// 指標節點

    // ++核心函式
    // 節點是從node節點出發, 一直尋找右節點的左孩子, 每次找到比上次大的元素
  void increment()
  {
      // 有右節點, 就往右節點走
    if (node->right != 0) {
      node = node->right;
        // 一直往左節點走, 直到走到頭
      while (node->left != 0)
        node = node->left;
    }
      // 沒有右節點, 就尋找父節點
    else {
      base_ptr y = node->parent;
        // 如果該節點是父節點的右孩子就繼續往上找, 直到y節點不是父節點的右孩子
      while (node == y->right) {
        node = y;
        y = y->parent;
      }
      if (node->right != y)
        node = y;
    }
  }
	
    // --核心程式碼
    // 節點是從node節點出發, 一直尋找左節點的右孩子, 每次找到比上次小的元素
  void decrement()
  {
      // 只有根節點, 每次--都是根節點
    if (node->color == __rb_tree_red && node->parent->parent == node)
      node = node->right;
      // 有左節點
    else if (node->left != 0) {
        // 往左節點走
      base_ptr y = node->left;
        // 只要有右節點就一直往右節點走
      while (y->right != 0)
        y = y->right;
      node = y;
    }
      // 沒有左節點
    else {
        // 尋找父節點
      base_ptr y = node->parent;
        // 如果當前節點是父節點的左孩子就繼續尋找父節點直到不再是左孩子
      while (node == y->left) {
        node = y;
        y = y->parent;
      }
      node = y;
    }
  }
};

__rb_tree_iterator 迭代器

template <class Value, class Ref, class Ptr>
struct __rb_tree_iterator : public __rb_tree_base_iterator	// 繼承__rb_tree_base_iterator
{
  typedef Value value_type;
  typedef Ref reference;
  typedef Ptr pointer;
  typedef __rb_tree_iterator<Value, Value&, Value*>             iterator;
  typedef __rb_tree_iterator<Value, const Value&, const Value*> const_iterator;
  typedef __rb_tree_iterator<Value, Ref, Ptr>                   self;
  typedef __rb_tree_node<Value>* link_type;

	// 建構函式
  __rb_tree_iterator() {}
  __rb_tree_iterator(link_type x) { node = x; }	// 初始化node節點
  __rb_tree_iterator(const iterator& it) { node = it.node; }	// 初始化node節點

	// 過載指標
  reference operator*() const { return link_type(node)->value_field; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
  pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */

	// 過載++與--操作, 呼叫increment和decrement函式
  self& operator++() { increment(); return *this; }
  self operator++(int) {
    self tmp = *this;
    increment();
    return tmp;
  }
    
  self& operator--() { decrement(); return *this; }
  self operator--(int) {
    self tmp = *this;
    decrement();
    return tmp;
  }
};

traits程式設計

#ifndef __STL_CLASS_PARTIAL_SPECIALIZATION
inline bidirectional_iterator_tag
iterator_category(const __rb_tree_base_iterator&) {
  return bidirectional_iterator_tag();
}

inline __rb_tree_base_iterator::difference_type*
distance_type(const __rb_tree_base_iterator&) {
  return (__rb_tree_base_iterator::difference_type*) 0;
}

template <class Value, class Ref, class Ptr>
inline Value* value_type(const __rb_tree_iterator<Value, Ref, Ptr>&) {
  return (Value*) 0;
}
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */

過載

// ==與!= 比較兩個tree的node是相同
inline bool operator==(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
  return x.node == y.node;
}
inline bool operator!=(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
  return x.node != y.node;
}

紅黑樹的調整最重要的就是用左旋和右旋.

左旋
inline void 
__rb_tree_rotate_left(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  __rb_tree_node_base* y = x->right;	// y為x的右孩子
  x->right = y->left;	// x的右孩子為y的左孩子
    // 如果y的左節點不為空
    // 將y的左節點的父節點指向x
  if (y->left != 0)
    y->left->parent = x;	
  // y的父節點指向x的父節點
  y->parent = x->parent;

    // 如果x就是為根節點
  if (x == root)
    root = y;
    // 如果x為父節點的左孩子
    // x的父節點的左節點指向y
  else if (x == x->parent->left)
    x->parent->left = y;
    // 如果x為父節點的右孩子
    // x的父節點的右節點指向y
  else
    x->parent->right = y;
  // y的左孩子指向x
  // x的父節點指向y
  y->left = x;
  x->parent = y;
}
右旋
inline void 
__rb_tree_rotate_right(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  __rb_tree_node_base* y = x->left;	// y為x的左節點
  x->left = y->right;	// x的左節點指向y的右節點
    // y有右孩子
    // y的的右孩子的父節點指向節點x
  if (y->right != 0)
    y->right->parent = x;
  // y的父節點指向x的父節點
  y->parent = x->parent;

    // 如果x為根節點
    // 將y改為根節點
  if (x == root)
    root = y;
    // x為父節點的右孩子
    // x的父節點的左節點指向y
  else if (x == x->parent->right)
    x->parent->right = y;
    // x的父節點的左節點指向y
  else
    x->parent->left = y;
  // y的右節點指向x
  // x的父節點指向y
  y->right = x;
  x->parent = y;
}
調整紅黑樹 :
  1. (如果) x不是根節點同時x的父節點顏色為紅色
    1. (如果) x的父節點是x的祖節點的左孩子
      1. 有y為x父節點的兄弟
      2. (如果) y節點存在並且顏色為紅色
        1. 將父節點和兄弟節點顏色都改為黑色
        2. 祖節點改為紅色
        3. 當前節點(x)為祖節點
      3. (否則) y節點不存在或顏色為黑色
        1. x是父節點的右孩子
        2. 當前節點為x的父節點
        3. 左旋
        4. x父節點顏色改為黑色
        5. 兄弟節點顏色改為紅色
        6. 以祖節點為節點右旋
    2. (否則) x的父節點是x的祖節點的右孩子
      1. 有y為x父節點的兄弟
      2. (如果) y存在並且顏色為紅色
        1. 將父節點和兄弟節點顏色都改為黑色
        2. 祖節點改為紅色
        3. 當前節點(x)為祖節點
      3. (否則) y節點不存在或顏色為黑色
        1. x是父節點的左孩子
        2. 右旋
        3. x父節點顏色改為黑色
        4. 兄弟節點顏色改為紅色
        5. 以祖節點為節點左旋
  2. (否則) 將根節點調整為黑色, 因為根節點可能會被修改
// 這裡x指的當前節點, 因為後面會修改到x, 描述x就會出問題
inline void 
__rb_tree_rebalance(__rb_tree_node_base* x, __rb_tree_node_base*& root)
{
  x->color = __rb_tree_red;	// 當前節點顏色改為紅色
    // x不是根節點同時x的父節點顏色為紅色
  while (x != root && x->parent->color == __rb_tree_red) {
      /************ 1 **********/
      // x的父節點是x的祖節點的左孩子
    if (x->parent == x->parent->parent->left) {
        // 有y為x父節點的兄弟
      __rb_tree_node_base* y = x->parent->parent->right;
        /********* a ***********/
        // y節點存在並且顏色為紅色
      if (y && y->color == __rb_tree_red) {
        // 將父節點和兄弟節點顏色都改為黑色
        // 祖節點改為紅色
        // 當前節點(x)為祖節點
        x->parent->color = __rb_tree_black;
        y->color = __rb_tree_black;
        x->parent->parent->color = __rb_tree_red;
        x = x->parent->parent;
      }
        /********* b ***********/
        // y節點不存在或顏色為黑色
      else {
          // x是父節點的右孩子
        if (x == x->parent->right) {
            // 當前節點為x的父節點
            // 左旋
          x = x->parent;
          __rb_tree_rotate_left(x, root);
        }
          // x父節點顏色改為黑色
          // 兄弟節點顏色改為紅色
          // 以祖節點為節點右旋
        x->parent->color = __rb_tree_black;
        x->parent->parent->color = __rb_tree_red;
        __rb_tree_rotate_right(x->parent->parent, root);
      }
    }
     /************ 2 **********/
    // x的父節點是x的祖節點的右孩子
    else {
        // 有y為x父節點的兄弟
      __rb_tree_node_base* y = x->parent->parent->left;
        /********* a ***********/
        // y存在並且顏色為紅色
      if (y && y->color == __rb_tree_red) {
          // 將父節點和兄弟節點顏色都改為黑色
          // 祖節點改為紅色
          // 當前節點(x)為祖節點
        x->parent->color = __rb_tree_black;
        y->color = __rb_tree_black;
        x->parent->parent->color = __rb_tree_red;
        x = x->parent->parent;
      }
        /********* b ***********/
        // y節點不存在或顏色為黑色
      else {
          // x是父節點的左孩子
        if (x == x->parent->left) {
            // 當前節點為x的父節點
            // 右旋
          x = x->parent;
          __rb_tree_rotate_right(x, root);
        }
          // x父節點顏色改為黑色
          // 兄弟節點顏色改為紅色
          // 以祖節點為節點左旋
        x->parent->color = __rb_tree_black;
        x->parent->parent->color = __rb_tree_red;
        __rb_tree_rotate_left(x->parent->parent, root);
      }
    }
  }
    // 將根節點調整為黑色, 因為根節點可能會被修改
  root->color = __rb_tree_black;
}
刪除節點
// 這裡x指的當前節點, 因為後面會修改到x, 描述x就會出問題
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)
{
    // y儲存z節點指標
  __rb_tree_node_base* y = z;
  __rb_tree_node_base* x = 0;
  __rb_tree_node_base* x_parent = 0;
    /******* 1 *********/
    // y不存在左節點
  if (y->left == 0)             // z has at most one non-null child. y == z.
      // 則當前節點修改為x的右孩子
    x = y->right;               // x might be null.
    //  y存在左節點
    /******* 2 *********/
  else
      /******* a *********/  
    // y不存在右節點
    if (y->right == 0)          // z has exactly one non-null child.  y == z.
        // 則當前節點修改為x的左孩子
      x = y->left;              // x is not null.
      /******* b *********/ 
    // y存在左右節點
    else {                      // z has two non-null children.  Set y to
       // y修改為y的右節點
       // 如果此時y的左節點存在, 就一直往左邊走
       // 當前節點為y的右節點
      y = y->right;             //   z's successor.  x might be null.
      while (y->left != 0)
        y = y->left;
      x = y->right;
    }
  /******* 1 *********/ 
  // 以上的操作就是為了找到z的邊的最先的那個節點為y
  // y節點被修改過
  if (y != z) {                 // relink y in place of z.  y is z's successor
      // z的左節點的父節點指向y
      // y的左節點指向x的左節點
    z->left->parent = y; 
    y->left = z->left;
      /******* a *********/
      // y不為z的右節點
    if (y != z->right) {
        // 儲存y的父節點
      x_parent = y->parent;
        // y的左或右節點存在(x), 則x的父節點指向y的父節點
      if (x) x->parent = y->parent;
       	// y的父節點指向的左孩子指向x
        // y的右孩子指向z的右孩子
        // z的右孩子的父節點指向y
      y->parent->left = x;      // y must be a left child
      y->right = z->right;
      z->right->parent = y;
    }
      // y是z的右節點
    else
      x_parent = y;  
      
      // z是根節點
    if (root == z)
      root = y;
      // z是父節點的左孩子
    else if (z->parent->left == z)
      z->parent->left = y;	// z的父節點的左孩子指向y
      // z是父節點的右孩子
    else 
      z->parent->right = y;	// z的父節點的右孩子指向y
      // y的父節點指向z的父節點
      // 交換y和z的節點顏色
      // y修改為z
    y->parent = z->parent;
    __STD::swap(y->color, z->color);
    y = z;
    // y now points to node to be actually deleted
  }
    /******* 2 *********/
    // y沒有被修改過
  else {                        // y == z
    x_parent = y->parent;
    if (x) x->parent = y->parent;   // x的父節點指向y的父節點
    if (root == z)
      root = x;
    else 
      if (z->parent->left == z)
        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;
    // makes leftmost == header if z == root
      else
        leftmost = __rb_tree_node_base::minimum(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);
  }
   /************ 1 **************/
    // y節點的顏色不為紅色
  if (y->color != __rb_tree_red) { 
      // x不為根節點並且x為空或顏色為黑色
      // 下面的分析與上面一樣, 這裡就不在做詳細的分析了, 只要分析的時候畫好圖就行了
    while (x != root && (x == 0 || x->color == __rb_tree_black))
      if (x == x_parent->left) {
        __rb_tree_node_base* w = x_parent->right;
        if (w->color == __rb_tree_red) {
          w->color = __rb_tree_black;
          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)) {
          w->color = __rb_tree_red;
          x = x_parent;
          x_parent = x_parent->parent;
        } else {
          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);
            w = x_parent->right;
          }
          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