stl中并没有堆这样一个类(但是有个priority_queue,这其实就是堆哦,stl跟大小比较相关的容器大都允许自定义比较函数哦,很不错的呢),但是在学习中我们可以模拟实现堆,把它封装为一个类,然后在运用当中调用它的各种接口,实现一次可以深入理解堆排序的原理和优势,使我们在面对各种问题的时候可以借助这个算法实现更好的优化。
接下来就让我们看一下具体的实现过程:
这里是头文件和命名空间的包函
#pragma once
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;
这里是模拟实现的堆数据结构
template<class T, class Compare>
class Heap
{
public:
Heap() //无参构造函数
{}
Heap(const T* a, size_t n) //使用数组初始化堆
{
size_t i = 0;
_a.reserve(n);
for (i = 0; i < n; i++)
{
_a.push_back(a[i]);
}
int parent = (n - 1) / 2;
for (parent; parent >= 0; parent--)
{
adjustdown(parent); //建堆需要实现的向下调整算法;
}
}
void Push(const T& x)
{
_a.push_back(x);
adjustup(_a.size()-1); //这是向上调整算法
}
void Pop()
{
assert(_a.size());
swap(_a[0],_a[_a.size()-1]);
_a.pop_back();
adjustdown(0);
}
protected:
void adjustup(int child)
{
assert(child < _a.size()&& child >0);
Compare cmp; //这里的cmp和下面向下调整算法得cmp都是一个仿函数类,用仿函数主要是为了实现代码的复用.
int parent = (child - 1) / 2;
while (child > 0)
{
if (cmp(_a[child] , _a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
parent = (parent - 1) / 2;
}
else
{
break;
}
}
}
void adjustdown(int parent)
{
Compare cmp;
size_t child = parent * 2 + 1;
while (child<_a.size())
{
if (child + 1 < _a.size() && cmp(_a[child + 1] , _a[child]))
child++;
if (cmp(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
代码中蕴含了很多算法,但是只有代码晦涩难懂。所以绘制了几幅图便于理解。
下面我们主要看一下adjustdown 和dijustup这两个函数的算法
这里我们就将堆中的基本算法实现了。有了这个算法思路或者实例化后的结构我们就可以实现排序、topk问题等利用别的算法实现起来很麻烦的问题。而文章中建堆方式使用了向下调整算法建堆 , 而还可以使用向上调整法建堆哦 , 给个提示 , 这种建堆方式叫做插入法,看懂代码之后 , 有兴趣可以调用一下adjust_up函数建个堆试试哦,超级简单的几行代码。