1. 程式人生 > >紅黑樹並沒有我們想象的那麼難(下)

紅黑樹並沒有我們想象的那麼難(下)

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

相關推薦

沒有我們想象那麼()

// sgi stl _Rb_tree 插入演算法 insert_equal() 實現. // 策略概述: insert_equal() 在紅黑樹找到自己的位置, // 然後交由 _M_insert() 來處理接下來的工作. // _M_insert() 會將節點插入紅黑樹中, 接著調整紅黑樹, // 維持性

沒有我們想象那麼(上)

紅黑樹並沒有我們想象的那麼難 上、下兩篇已經完成, 希望能幫助到大家. 紅黑樹並沒有想象的那麼難, 初學者覺得晦澀難讀可能是因為情況太多. 紅黑樹的情況可以通過歸結, 通過合併來得到更少的情況, 如此可以加深對紅黑樹的理解. 網路上的大部分紅黑樹的講解因為沒有「合併」. 紅黑樹的五個

,並非想象中的那麼複雜

紅黑樹是非常popular的一種self-adjusted的平衡二叉排序樹。通常他給我們的印象是很複雜,有很多case,要小心的旋轉。有人說,曾將在某公司的面試時,被要求實現紅黑樹。他覺得這很沒有道理,幾乎很少有人能在不參考教科書的情況下,記清楚那麼多的case。在這一章裡,

「碼農讀書」:我們沒有自己想象那麼理性

作為碼農/程式設計師,我們經常認為自己是非常理性的。其實不僅是我們自己這麼認為,甚至我們身邊的朋友們在談到對我們的評價時,在聊到對程式設計師這個職業的印象時,都會普遍的認為這是一個非常理性、思維邏輯非常清晰的群體。但事實真的是這樣嗎? 我曾經也非常自信於自己的理性。但最近看了一本大塊頭書《思考,快與慢

真的沒你想的那麼

寫本文的原由是昨晚做夢居然夢到了在看原始碼,於是便有了此文...... 雖然標題是關於紅黑樹的,不過此文是結合圖片,通過分析TreeMap的原始碼,讓你理解起來不是那麼枯燥(前方高能,此文圖片眾多,慎入)。 作者 | 馬雲飛 責編 | 胡巍巍

Java轉python機器學習,沒有大家想象那麼美好!

輾轉幾年Java開發,換了幾份工作,沒一個穩定的學習、工作過程。中間也相親幾次,都是沒啥結果。換工作頻繁也嚴重打亂了和姑娘接觸的節奏。糟心工作連著遇到幾次,也怪自己眼光有問題。 2018也找了2次工作,中間有4、5個月沒有工作。看了個世界盃,看了個亞運會。也怪自己這段時間一直是換工作、找工作,節

真的沒你想的那麼

概述 TreeMap是紅黑樹的java實現,紅黑樹能保證增、刪、查等基本操作的時間複雜度為O(lgN)。 首先我們來看一張TreeMap的繼承體系圖: 還是比較直觀的,這裡來簡單說一下繼承體系中不常見的介面NavigableMap和SortedMa

死磕Java:J.U.C之ConcurrentHashMap轉換分析

作者:chessy來源:Java技術驛站在【死磕Java併發】-----J.U.C之Java併發

數據結構(5) 第五天 快速排序、歸排序、堆排序、高級數據結構介紹:平衡二叉、B/B+

平衡二叉樹 let b+樹 堆排 mark 9.png 思想 incr 相等 01 上次課程回顧 希爾排序 又叫減少增量排序 increasement = increasement / 3 + 1 02 快速排序思想 思想: 分治法 + 挖坑

遠端辦公,也許沒有想象那麼輕鬆

春節假期結束已經一個多星期了,隨著疫情的逐漸控制,很多企業陸陸續續開啟了復工模式,但為了安全考慮,有一部分企業仍然採取延遲復工的策略,比如大部分的網際網路公司,像我們熟悉的阿里、騰訊就規定復工時間延遲到2月17日,而企業的日常工作就通過遠端辦公的方式展開,藉著這次東風,很多人體驗到了遠端辦公的魅力之處,更有甚

【bzoj3227】

發現 blog ret amp 這樣的 spa 兩個 include log 神TM的紅黑樹,其實本質上應該還是一種樹dp的問題…… 一開始想了一個比較裸的樹dp,後來發現還有更強的做法。 每個前端黑節點是看作一個物品,然後這就是很典型的樹形dp的問題。 不過可以這麽考慮,

圖解集合7:概念、的插入及旋轉操作詳細解讀

集合 得到 2個 排序。 數據流 except boolean 修正 split 原文地址http://www.cnblogs.com/xrq730/p/6867924.html,轉載請註明出處,謝謝! 初識TreeMap 之前的文章講解了兩種Map,分別是HashMa

C++實現

con colors end ase 復制代碼 設置 typename ucc 技術 1 /* 2 * rbtree.h 3 * 1. 每個節點是紅色或者黑色 4 * 2. 根節點是黑色 5 * 3. 每個葉子節點是黑色(該葉子節點就空的節點)

數據結構學習筆記-排序/隊/棧/鏈/堆/查找/

算法 數據結構排序:插入排序:每次從剩余數據中選取一個最小的,插入已經排序完成的序列中合並排序:將數據分成左右兩組分別排序,然後合並,對每組數據的排序遞歸處理。冒泡排序:重復交換兩個相鄰元素,從a[1]開始向a[0]方向冒泡,然後a[2]...當a[i]無法繼續往前擠的時候說明前面的更小了,而且越往前越小(擠

查找(一)史上最簡單清晰的解說

ont 演示 detail align article 向上 節點 動態插入 列表 查找(一) 我們使用符號表這個詞來描寫敘述一張抽象的表格。我們會將信息(值)存儲在當中,然後依照指定的鍵來搜索並獲取這些信息。鍵和值的詳細意義取決於不同的應用。 符號表中可能會保

教你透徹了解

black ade 我們 工作 key 針對 otn strong lean 教你透徹了解紅黑樹 作者:July、saturnman 2010年12月29日 本文參考:Google、算法導論、STL源碼剖析、計算機程序設計藝術。 推薦閱讀: Left-

else if 滿足 編碼 使用 由於 imap ica 困難 十分 轉自:http://www.cnblogs.com/yangecnu/p/Introduce-2-3-Search-Tree.html 前面一篇文章介紹了2-3查找樹,可以看到,2-3查找樹能保證在插

與AVL

target 相同 spa search htm 解決 evel 所有應用 二叉搜索樹 概述:本文從排序二叉樹作為引子,講解了紅黑樹,最後把紅黑樹和AVL樹做了一個比較全面的對比。 1 排序二叉樹 排序二叉樹是一種特殊結構的二叉樹,可以非常方便地對樹中所有節點進行排

------ luogu P3369 【模板】普通平衡(Treap/SBT)

div child lin main false tchar clas char als 二次聯通門 : luogu P3369 【模板】普通平衡樹(Treap/SBT) 近幾天閑來無事。。。就把各種平衡樹都寫了一下。。。 下面是紅黑樹(Red Black Tree)

關聯容器set的用法(關聯容器,,)

ise 特定 using iter tor pre .com main com set和multiset會根據特定的排序準則自動將元素排序,set中元素不允許重復,multiset可以重復。// 2017/7/23號 好像set容器裏面只能裝一個元素#include<