《STL原始碼剖析》-- stl_heap.h

凝霜發表於2011-08-01
// Filename:    stl_heap.h

// Comment By:  凝霜
// E-mail:      mdl2009@vip.qq.com
// Blog:        http://blog.csdn.net/mdl13412

/*
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Hewlett-Packard Company makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * Copyright (c) 1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  Silicon Graphics makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 */

/* NOTE: This is an internal header file, included by other STL headers.
 *   You should not attempt to use it directly.
 */

#ifndef __SGI_STL_INTERNAL_HEAP_H
#define __SGI_STL_INTERNAL_HEAP_H

__STL_BEGIN_NAMESPACE

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1209
#endif

////////////////////////////////////////////////////////////////////////////////
// 注意: push_heap()操作前要保證新新增的元素已經加入到容器末尾!!!
////////////////////////////////////////////////////////////////////////////////
// 下面是使用預設比較函式的一個例項, XXX代表需要調整結點的位置
// 執行插入前, 元素已經追加到容器尾, 其值為450, 這裡我們只
// 關注其位置, 不表示出其數值
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [XXX]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | XXX | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 下面是移動步驟及記憶體變化
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------                -----------------------
//        |                     |                |                     |
//      [200]                 [XXX]-------     [350]                 [240]
//        |                     |        |
//   -----------           -----------   |
//   |         |           |         |   |  調整元素位置
// [150]     [130]       [120]     [270]--
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | XXX | 350 | 240 | 150 | 130 | 120 | 270 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [XXX]------------- 交換         [400]
//                   |              |                |
//        -----------------------   |     -----------------------
//        |                     |   |     |                     |
//      [200]                 [300]--   [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [270]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | XXX | 400 | 200 | 300 | 350 | 240 | 150 | 130 | 120 | 270 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 現在滿足heap的要求了, 對[XXX]直接賦值即可
//
////////////////////////////////////////////////////////////////////////////////

template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
                 Distance topIndex, T value)
{
  // 首先找出待處理元素的父結點
  Distance parent = (holeIndex - 1) / 2;

  // 判斷當前待處理結點是否優先順序高於其父結點, 如果是則將其父結點向下移動
  // 設定當前結點為父結點位置, 繼續, 直到優先順序小於父結點或者已經到達heap頂端
  while (holeIndex > topIndex && *(first + parent) < value) {
    *(first + holeIndex) = *(first + parent);
    holeIndex = parent;
    parent = (holeIndex - 1) / 2;
  }

  // 將找到的合適的位置設定成正確值
  *(first + holeIndex) = value;
}

template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
                            RandomAccessIterator last, Distance*, T*)
{
  // 因為first所指的那個元素不是heap的組成元素, 所以計算距離要減去1
  __push_heap(first, Distance((last - first) - 1), Distance(0),
              T(*(last - 1)));
}

// 呼叫此函式前要先把待處理元素追加到容器末尾
template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __push_heap_aux(first, last, distance_type(first), value_type(first));
}

template <class RandomAccessIterator, class Distance, class T, class Compare>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
                 Distance topIndex, T value, Compare comp)
{
  Distance parent = (holeIndex - 1) / 2;
  while (holeIndex > topIndex && comp(*(first + parent), value)) {
    *(first + holeIndex) = *(first + parent);
    holeIndex = parent;
    parent = (holeIndex - 1) / 2;
  }
  *(first + holeIndex) = value;
}

template <class RandomAccessIterator, class Compare, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
                            RandomAccessIterator last, Compare comp,
                            Distance*, T*)
{
  __push_heap(first, Distance((last - first) - 1), Distance(0),
              T(*(last - 1)), comp);
}

// 這個除了使用者自己指定優先順序決策判別式外和預設的無區別
template <class RandomAccessIterator, class Compare>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last,
                      Compare comp)
{
  __push_heap_aux(first, last, comp, distance_type(first), value_type(first));
}

////////////////////////////////////////////////////////////////////////////////
// 注意: pop_heap()操作, 執行完操作後要自己將容器尾元素彈出
////////////////////////////////////////////////////////////////////////////////
// 這裡以預設的heap優先順序決策來說
// STL採用的是先將待pop的元素複製到heap尾部, 然後將整個heap向上調整
// 這樣就會將最後空出一個hole, 將原來最後的元素在這裡進行push()操作
// 這就是兩個shift_up的過程
// 個人感覺使用使用shift_down的演算法更高效, 雖然時間複雜度一樣, 但是shift_down
// 進行操作的元素會更少,
// 之所以用shift_up這可能也是STL設計理念的問題吧, 能複用就不寫新的^_^
////////////////////////////////////////////////////////////////////////////////
// 下面是使用預設比較函式的一個例項, 我們要彈出的是優先順序最高的元素[500]
// 首先要把彈出的元素[500]複製到heap末尾
// 然後進行第一次shift_up, 完成後進行push()操作, 這個就是第二次shift_up了
//
//                                 [500]
//                                   |
//                   ---------------------------------
//                   |                               |
//                 [300]                           [400]
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [100]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 100 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 下面是移動步驟及記憶體變化                      複製
//                                 [500]----------------------------------
//                                   |                                   |
//                   ---------------------------------                   |
//                   |                               |                   |
//                 [300]                           [400]                 |
//                   |                               |                   |
//        -----------------------         -----------------------        |
//        |                     |         |                     |        |
//      [200]                 [270]     [350]                 [240]      |
//        |                     |                                        |
//   -----------           -----------                                   |
//   |         |           |         |                                   |
// [150]     [130]       [120]     [500]----------------------------------
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 500 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [400]-----------------------
//                                   |                        |
//                   ---------------------------------        | shift_up
//                   |                               |        |
//                 [300]                           [400]-------
//                   |                               |
//        -----------------------         -----------------------
//        |                     |         |                     |
//      [200]                 [270]     [350]                 [240]
//        |                     |
//   -----------           -----------
//   |         |           |         |
// [150]     [130]       [120]     [500]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 400 | 300 | 400 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
//                                 [400]
//                                   |
//                   ---------------------------------
//                   |                               |        shift_up
//                 [300]                           [350]-------------------
//                   |                               |                    |
//        -----------------------         -----------------------         |
//        |                     |         |                     |         |
//      [200]                 [270]     [350]                 [240]       |
//        |                     |         |                               |
//   -----------           -----------    ----------------------------------
//   |         |           |         |
// [150]     [130]       [120]     [500]
//
// first                                                                      last
// ↓                                                                           ↓
// --------------------------------------------------------------------------------------------
// | Not Use | 400 | 300 | 350 | 200 | 270 | 350 | 240 | 150 | 130 | 120 | 500 | ...... | end |
// --------------------------------------------------------------------------------------------
//
// 接下來就是push()操作了, 參考前面的push()
////////////////////////////////////////////////////////////////////////////////

template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
                   Distance len, T value)
{
  Distance topIndex = holeIndex;
  Distance secondChild = 2 * holeIndex + 2;     // 彈出元素的有子孩

  // 調整heap元素位置
  while (secondChild < len) {
    // 選擇兩個子孩中較大的進行操作, 使用secondChild表示其偏移
    if (*(first + secondChild) < *(first + (secondChild - 1)))
      secondChild--;

    // 將較大元素向上填充, 並將整體偏移向下調整, 繼續調整
    *(first + holeIndex) = *(first + secondChild);
    holeIndex = secondChild;
    secondChild = 2 * (secondChild + 1);
  }

  if (secondChild == len) {
    *(first + holeIndex) = *(first + (secondChild - 1));
    holeIndex = secondChild - 1;
  }

  // 這裡就是shift_up過程了, 將最初的heap末尾元素向上調整
  // 侯捷老師對這裡的理解有誤, :-), 人非聖賢, 孰能無過, ^_^
  __push_heap(first, holeIndex, topIndex, value);
}

template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Distance*)
{
  // 將彈出的元素調整到heap末尾, 這個元素需要使用者手動彈出
  *result = *first;

  // 去掉末尾哪個彈出的元素, 調整heap
  __adjust_heap(first, Distance(0), Distance(last - first), value);
}

template <class RandomAccessIterator, class T>
inline void __pop_heap_aux(RandomAccessIterator first,
                           RandomAccessIterator last, T*)
{
  __pop_heap(first, last - 1, last - 1, T(*(last - 1)), distance_type(first));
}

template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __pop_heap_aux(first, last, value_type(first));
}

template <class RandomAccessIterator, class Distance, class T, class Compare>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
                   Distance len, T value, Compare comp)
{
  Distance topIndex = holeIndex;
  Distance secondChild = 2 * holeIndex + 2;
  while (secondChild < len) {
    if (comp(*(first + secondChild), *(first + (secondChild - 1))))
      secondChild--;
    *(first + holeIndex) = *(first + secondChild);
    holeIndex = secondChild;
    secondChild = 2 * (secondChild + 1);
  }
  if (secondChild == len) {
    *(first + holeIndex) = *(first + (secondChild - 1));
    holeIndex = secondChild - 1;
  }
  __push_heap(first, holeIndex, topIndex, value, comp);
}

template <class RandomAccessIterator, class T, class Compare, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                       RandomAccessIterator result, T value, Compare comp,
                       Distance*)
{
  *result = *first;
  __adjust_heap(first, Distance(0), Distance(last - first), value, comp);
}

template <class RandomAccessIterator, class T, class Compare>
inline void __pop_heap_aux(RandomAccessIterator first,
                           RandomAccessIterator last, T*, Compare comp)
{
  __pop_heap(first, last - 1, last - 1, T(*(last - 1)), comp,
             distance_type(first));
}

template <class RandomAccessIterator, class Compare>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last,
                     Compare comp)
{
    __pop_heap_aux(first, last, value_type(first), comp);
}

// 這個沒設麼好說的, 參考上面的分析吧
template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
                 Distance*)
{
  if (last - first < 2) return;
  Distance len = last - first;
  Distance parent = (len - 2)/2;

  while (true) {
    __adjust_heap(first, parent, len, T(*(first + parent)));
    if (parent == 0) return;
    parent--;
  }
}

template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  __make_heap(first, last, value_type(first), distance_type(first));
}

template <class RandomAccessIterator, class Compare, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last,
                 Compare comp, T*, Distance*)
{
  if (last - first < 2) return;
  Distance len = last - first;
  Distance parent = (len - 2)/2;

  while (true) {
    __adjust_heap(first, parent, len, T(*(first + parent)), comp);
    if (parent == 0) return;
    parent--;
  }
}

template <class RandomAccessIterator, class Compare>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last,
                      Compare comp)
{
  __make_heap(first, last, comp, value_type(first), distance_type(first));
}

// 這個能保證heap有序, 其實個人感覺沒啥必要, 這樣還不如直接用平衡二叉樹
template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last)
{
  while (last - first > 1) pop_heap(first, last--);
}

template <class RandomAccessIterator, class Compare>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last,
               Compare comp)
{
  while (last - first > 1) pop_heap(first, last--, comp);
}

#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1209
#endif

__STL_END_NAMESPACE

#endif /* __SGI_STL_INTERNAL_HEAP_H */

// Local Variables:
// mode:C++
// End:

相關文章