C++堆的实现


堆是一个完全二叉树。
堆根据元素的排列方式,可以分为最大堆(max-heap)和最小堆(min-heap),其中:

  • 最大堆:是最大的完全二叉树,其每个节点的值都大于或等于其子节点。所以在最大堆中,根结点是该完全二叉树的最大结点。

  • 最小堆:是最小的完全二叉树,其每个节点的值都小于或等于其子节点。所以在最小堆中,根结点是该完全二叉树的最小结点。


堆并不归属于STL库容器组件,而是作为优先队列(priority queue)的底层实现。堆是一种完全二叉树,整棵二叉树除了最底层的叶子节点之外是填满的,而最底层的叶子节点由左至右是没有空隙的,所以我们可以利用array 或者vector来存储所有的节点。
堆算法在头文件algorithm中实现,主要操作有:push_heap算法,pop_heap算法,sort_heap算法和make_heap算法,一下以最大堆为例子,逐一介绍:


  • push_heap算法
    实现思路:将新插入的元素插入到底层vector的end()处,此时为了满足最大堆的条件,需要执行一个向上回溯的操作,将新节点与父节点比较,如果其键值大于父节点,就交换位置,如此一直向上回溯,直到不需要交换位置为至。
    注意:该方法接受一个仿函数(自定义比较规则,如果想使用最小堆,可以传入参数:greater());或者两个迭代器,代表vector的 头尾,并且新元素以及插入到底层容器的末尾,否则可能导致不可预期的结果。
  • pop_heap算法
    实现思路:pop_heap操作实际是取走根结点,移至底层容器的最后一个位置(也就是交换根结点和最后的叶子节点),此时为了满足最大堆的条件,需要执行一个向下回溯的操作,将根节点与孩子节点点比较,如果其键值小于孩子节点,就与较大孩子节点交换位置,如此一直向下回溯,直到不需要交换位置为至或到叶子节点。
    STL库中原型:
    图2,pop_heap原型
    注意:该方法接受一个仿函数(自定义比较规则,如果想使用最小堆,可以传入参数:greater());或者两个迭代器,代表vector的 头尾。
  • sort_heap算法
    思想思路:pop_heap操作,每次将最大元素放到底层容器末尾,如果重复调用pop_heap操作,便可以获得一个递增序列。堆排序的时间复杂度,最坏(O(nlogn )),最好(O(nlogn )),平均(O(nlogn )),不稳定排序算法。
    注意:该方法接受一个仿函数(自定义比较规则,如果想使用最小堆,可以传入参数:greater());或者两个迭代器,代表vector的 头尾。
  • make_heap算法
    实现思路:make_heap操作,用于将已经存在n个元素的序列构建成最大堆,其核心操作为筛选法。如果对于一个空序列而言,可以重复调用push_heap操作,构建最大堆。
    注意:该方法接受一个仿函数(自定义比较规则,如果想使用最小堆,可以传入参数:greater());或者两个迭代器,代表vector的 头尾。

STL中堆的应用:


  1. 建堆
    make_heap(_first,_last,_comp)
    默认建立最大堆,如果要建小跟堆要在比较函数中打大于号(>),注意,是大于号
  2. 在堆中添加数据
    push_heap(_first,_last)
    要现在容器中加如数据,再调用push_heap();
  3. 在堆中删除数据
    pop_heap(_first,_last)
    要先调用pop_heap(),再在容器中删除数据;
  4. 堆排序
    sort_heap(_first,_last,)
    排序后就不是一个合法的堆了,堆排序大家应该都懂吧

//待完善

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
void print_ivec(vector<int>::iterator begin, vector<int>::iterator end)
{
    for(;begin != end; ++begin)
        cout << *begin << '\t';
    cout << endl;
}

//建立最小堆的比较函数
bool cmp(int a,int b)
{ 
  return a>b;
}

int main(int argc, char* argv[])
{
    int a[] = {1, 12, 15, 20, 30};
    vector<int> ivec(a, a + sizeof(a) / sizeof(a[0]));
    print_ivec(ivec.begin(), ivec.end());

    //建堆  最小堆
    make_heap(ivec.begin(), ivec.end(),cmp);   
    print_ivec(ivec.begin(), ivec.end());
    //在堆中删除元素
    pop_heap(ivec.begin(), ivec.end(),cmp);
    ivec.pop_back();

    print_ivec(ivec.begin(), ivec.end());

    //在堆中添加元素
    ivec.push_back(0);
    push_heap(ivec.begin(), ivec.end(),cmp);

    print_ivec(ivec.begin(), ivec.end());

    //排序  为什么需要加入比较函数。。。不加会报错。。。
    sort_heap(ivec.begin(), ivec.end(),cmp);  //但是此时就破坏了堆
    print_ivec(ivec.begin(), ivec.end());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39540045/article/details/80504753
今日推荐