c++: c++ make_heap, pop_heap, push_heap, sort_heap详解

std::pop_heap是一个实现快排的库

将front(即第一个最大元素)移动到end的前部,同时将剩下的元素重新构造成(堆排序)一个新的heap。

时间复杂度是: (2*log(last - first))

例如:

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
int main()
{
  int a[] = {100, 19, 36, 17, 3, 25, 1, 2, 7};
  vector<int> v(a, a+9);
 
  cout <<"\nHere are the values in the vector (the heap):\n";
  for (vector<int>::size_type i=0; i<v.size(); i++)
    cout <<v.at(i)<<" ";
 
  cout <<"\nNow we delete (pop) a value from the heap.";
  pop_heap(v.begin(), v.end());


  cout <<"\nHere are the revised contents of the vector:\n";
  for (vector<int>::size_type i=0; i<v.size(); i++)
   cout <<v.at(i)<<" ";
  cout <<"\nNote that the value deleted from the heap is still "
         "\nin the vector (at the end of the vector).";
 
  cout <<"\nSo, we should reduce the size of the vector by 1, which "
         "we now do, and\nthen display the vector, which is once again a heap, one more time.";
 
  v.resize(v.size()-1);
  cout <<"\nHere are the final contents of the vector:\n";
  for (vector<int>::size_type i=0; i<v.size(); i++)
    cout <<v.at(i)<<" ";
 
  return 0;
}

其结果是:

Output:

Here are the values in the vector (the heap):
100 19 36 17 3 25 1 2 7

Now we delete (pop) a value from the heap.

Here are the revised contents of the vector:
36 19 25 17 3 7 1 2 100
Note that the value deleted from the heap is still
in the vector (at the end of the vector).

So, we should reduce the size of the vector by 1, which we now do, and
then display the vector, which is once again a heap, one more time.

Here are the final contents of the vector:
36 19 25 17 3 7 1 2




理论---堆简介

堆并不是STL的组件,但是经常充当着底层实现结构。比如优先级队列(Priority Queue)等等。

堆是一种完全二叉树,因此我们可以用数组来存储所有节点。在这里的实现中,采用了一个技巧:将数组中索引为0的元素保留,设置为极大值或者为极小值(依据大顶堆或者小顶堆而定)。那么当某个节点的索引是i时,其左子节点索引为2*i,右子节点索引为2*i+1.父节点是i/2(这里/表示高斯符号,取整)。这种以数组表示树的方式,我们成为隐式表述法(implicit reprentation)。我们这里用C++ STL中的容器vector实现替代数组的功能。



堆的主要相关算法介绍


push_heap算法

此操作是向堆中添加一个节点。为了满足完全二叉树的条件,新加入的元素一定放在最下面的一层作为叶节点,并填补在由左至右的第一个空格,在这里放在底层容器vector的end()处。

很显然,新元素的加入很可能使得堆不在满足大顶堆的性质---每个节点的键值都大于或等于其子节点的键值。为了调整使得其重新满足大顶堆的特点,在这里执行一个上溯(percolate up)操作:将新节点与父节点比较,如果其键值比父节点大,就交换父子的位置,如此一直上溯,直到不需要交换或者到根节点为止。


pop_heap算法

此操作取走根节点,放在底端。然后对比第二层的B、C大(假设对比规则是“a>b”)的枝干放在前面,小的放在后面。

例如:

如果: B > C

则结果是: BDEHIJ + CFG + A

PS:只对比一次


make_heap算法

此操作是依据已有的各元素构建堆。

其中,各元素已经存放在底层容器vector中。

构建堆实质是一个不断调整堆(即前面pop_heap算法中的调整堆的操作)的过程---通过不断调整子树,使得子树满足堆的特性来使得整个树满足堆的性质。

叶节点显然需要调整,第一个需要执行调整操作的子树的根节点是从后往前的第一个非叶结点。从此节点往前到根节点对每个子树执行调整操作,即可构建堆。


sort_heap算法

堆排序算法。执行此操作之后,容器vector中的元素按照从小到大的顺序排列。

构建大顶堆之后,不断执行pop_heap算法取出堆顶的元素,即可。因为每次取得的是最大的元素,将其放在容器vector的最尾端。所以到最后vector中的元素是从小到大排列的。

PS: sort_heap时,必须先是一个堆(两个特性:1、最大元素在第一个 2、添加或者删除元素以对数时间),因此必须先做一次make_heap.


展开阅读全文

没有更多推荐了,返回首页