C++的堆操作一共两套体系:
make_heap和priority_queue
前者是直接把原容器变成堆,类似于python中的heapq模块,
后者是新建一个priority_queue类容器。
默认都是大顶堆,默认的cmp都是less<>.
想要小顶堆可以把val都加负号,也可以传入greater<>,同时也可以自定义:创建一个struct,或写一个cmp函数传入。
1. make_heap
头文件:
#include <algorithm>
堆操作方法-更底层,支持复杂度O(n)的直接建堆。
数据结构不改变,是直接将原先容器中的元素order变成符合堆定义的order
1.1 默认大顶堆,即cmp位置传入 "less<>{}"
vector<int> v { 3, 2, 4, 1, 5, 9 };
// 建堆:传入iterator
// make_heap(iterator::left, iterator::right, cmp = less<>);
make_heap(v.begin(),v.end()); //默认max-heap
// after make_heap, v: 9 5 4 1 2 3
// 弹出:先pop_heap再pop_back
pop_heap(v.begin(),v.end());
// after pop_heap, v: 5 3 4 1 2 9
// 把堆顶元素放到堆尾去了
// 此时非堆尾元素构成一个堆
int top = v.back; // top = 9
v.pop_back();
// v仍然是一个vector<int>,支持pop_back
// after removing the former top element, v: 5 3 4 1 2
// 压入:先push_back再push_pop
v.push_back(6);
push_heap(v.begin(),v.end());
1.2 快捷改成小顶堆
1.2.1 先把数据都添加负号再建堆
1.2.2 cmp位置传入 "greater<>{}"
make_heap(v1.begin(), v1.end(), greater<>{});
//注意pop_heap和push_heap时同样也要传
pop_heap(v1.begin(), v1.end(), greater<>{});
push_heap(v1.begin(), v1.end(), greater<>{});
1.3 传入其他cmp自定义
2. priority_queue
#include <queue>
封装的更好。但是不支持直接建堆。
数据结构改为优先队列类型,与vector<>接口不能共用。
2.1 默认是传入less<>,是最大堆
priority_queue<int> res;
res.push(x);
2.2 可以传入greater<> 改成最小堆,或将数据添加负号
priority_queue<int, vector<int>, greater<> > res;
res.push(x);
// 或
priority_queue<int> res;
res.push(-x);
2.3 自定义结构
struct S{
int key, val;
//一定是重写 < 操作符!!
bool operator<(const &S other){
return val < other.val;
// 或其他的判别方式,但反正大的那个意味着更高的优先级
}
priority_queue<S> res;
res.push({key,val});//此时便会自动用S中重写的<当作cmp函数去排序
}
3. topK问题
想要找到数组中第k或前k大元素,可以用heap来解决(当然也有其他更好的办法例如用快排的partition可以实现O(n)的复杂度,这里不再赘述)
一. 维护n个元素的堆
直接建max-heap,然后弹出来k个
复杂度为O(n + klgn)
二. 维护k个元素的堆
对前k个建min-heap。后面的每个与堆顶比较,把大的留在堆里,这样最后就剩下了前k个最大的元素,第k个就是堆顶那个。
复杂度为O(k + (n-k)lgk) 或者 O(nlgk)
(主要是看前k个是直接建堆还是插入建堆)