STL之heap(一)

       heap并不属于STL容器组件,它是个幕后英雄,扮演priority queue的助手。顾名思义,priority queue允许用户以任何次序将元素推入容器内,但取出来时一定是从优先权最高的元素开始取。

       list可以作为priority queue的底层机制,元素插入操作可享常数时间。但是找到list的最大值,却需要对整个list进行线性扫描。可以改变做法,元素插入前先经过排序一关,使得list的元素值总是由小到大(或由大到小),这样一来,取极值和删除可以达到常数时间,但是插入确是线性时间

       RB-tree可以实现priority queue的底层机制。元素的插入和极值的取得就是O(lgn)的表现,但是杀鸡用牛刀,未免小题大做,一来binary search tree的输入需要满足足够的随机性,二来binary search tree并不容易实现。priority queue的复杂度,最高介于queue和binary search tree之间,这样, binary heap便是这种条件最好下最好的候选人。

        现在分别介绍push_heap,pop_heap,sort_heap,make_heap相关算法。

        push_heap:

      为了满足complete binary tree的条件,新加入的元素一定要放在最下一层作为叶子节点,并填补在由左至右的第一个空格,也就是把新元素插入到底层的vector的end()处。新元素是否合适于其现有位置呢?为满足max-heap的条件,执行一次所谓的precolate up程序:将新节点拿来与其父节点比较,如果其键值比父节点大,就父子交换位置,如此一直上溯,直到不需要对换或直到根节点为止。示意图如下:

        

      下面便是push_heap算法的实现细节。该函数接受两个迭代器,用来表示一个heap底部容器(vector)的头尾,并且新元素已经插入底部容器的最尾端。如果不符合这两个条件,push_heap的执行结果未可预知。  

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>
inline void __push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance*, T*)
{
  __push_heap(first, Distance((last-first)-1), Distance(0), T(*(last-1)));
 //以上系根据implicit representation heap的结构特性:新值必置于底部容器的最尾端,此即第一个洞号:(last-first)-1
}
template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex, Distance topIndex, T value)
{ 
  //当尚未到达顶端,且父节点小于新值
  //由于以上使用operator <, 可知STL heap是一种max-heap
  Distance parent = (holeIndex-1)/2;
  while(holeIndex>topIndex&&*(first+parent)<value)
  {
    *(first+holeIndex) = *(first+parent);  //令洞值为父值
    holeIndex = parent;                    //调整洞号,向上提升至父节点
    parent = (holeIndex-1)/2;              //新洞的父节点
  }  //持续至顶端,或满足heap的次序特性为止
  *(first+holeIndex) = value; //令洞值为新值,完成插入操作
}

       pop_heap:

       max-heap,最大值必然在根节点。pop操作取走根节点(其实是设置底部容器vector的尾端节点)后,为了满足complete binary tree的条件,必须割舍最下层最右边的叶节点,并将其值重新安插至max-heap(因此有必要重新调整heap结构)。

       为满足max-heap次序特性,我们执行percolate down下溯程序:将空洞节点和其较大子节点“对调”,并持续下放,直至叶节点为止。然后将前述割舍之元素值设给这个“已到达叶层的空洞节点”,再对它执行一次percolate up上溯程序。

      

      pop_heap算法的实现细节如下,该函数接受两个迭代器,用来表现一个heap底部容器的头尾。如果不符合这个条件,pop_heap的执行结果未可预知。   

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

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));
 //pop操作的结果应为底部容器的第一个元素,因此,首先设定欲调整值为尾值,然后将首值调整至尾节点,然后重新调整
 //[first, last-1),使之重新成一个合格的heap
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator result,
T value, Distance*
){
  *result = *first;
  //设定尾值为首值,于是尾值即为欲求结果
  //可由客户端稍后再以底层容器pop_back()取出尾值
  __adjust_heap(first, Distance(0), distance(last-first), value);
  //以上欲重新调整heap,洞号为0,欲调整值为value
}
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;
  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+(childChild-1));
    holeIndex = secondChild - 1;
  }

  __push_heap(first, holeIndex, topIndex, value);
}
      注意,pop_heap之后,最大元素只是被置放于底部容器的最尾端,尚未被取走。如果要取其值,可使用底部容器vector所提供的back操作函数。如果要移除它,可使用底部容器vector所提供的pop_back操作函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值