堆
堆
世界上目前有很多种堆,比如二叉堆、斐波那契堆、配对堆、左 偏树等等,不过由于各种原因,OI里面提到的堆都是二叉堆,也 就是下面要讲的。下文的堆都是指的二叉堆。堆可以在单次严格O(logn)的时间复杂度内 插入一个数字、删除最大/小的数字,并严格O(1)询问最大/小的数字、
看起来,堆能做的事情非常有限,实际上,堆的应用十分广泛,它可以用来优化图论中的问题,比如Dijkstra堆优化、Prim堆优化等,堆还可以用来排序,这就是常见的堆排序,堆排序是一个不稳定排序,时间复杂度是稳定的O(log2n)。
关于堆的实现,手写堆的常数更低,会快一点,但是那很难写(和线段树的模板差不多长),在STL中,有现成的堆用,干嘛还要手写呢。
堆排序是很好写的,我愿意封他为“除了sort()以外最好用的排序”,下面是一个从大到小的堆排序模板:
#include<bits/stdc++.h>
using namespace std;
int a,ans[100],n;
priority_queue<int> h;
int main() {
cin>>n;
for(int i=1; i<=n; i++) cin>>a,h.push(a);//输入并入堆
for(int i=1; i<=n; i++) ans[i]=h.top(),h.pop();//入堆后,自动排序,再每次取堆头即可。
for(int i=1; i<=n; i++) cout<<ans[i]<<' ';
return 0;
}
STL建立堆的方式很简单,所谓堆,实际上就是优先队列
priority_queue<Type> h;
这就建好了一个优先队列,它是一个大根堆,如果想开一个小根堆,那么就稍微有一点长:
priority_queue<Type,vector<Type>,greater<Type> >
注:这里的Type是类型,什么类型都行,可以是关键字类型(如int,long long,char),class类型,用户自定义类型(结构体)。
STL的堆支持以下操作:
h.push(x);//往堆里插入x元素。 h.top()//堆头元素。 h.pop()//弹出堆头。 h.size()//堆内元素的个数。
说完了,上例题:P3871 [TJOI2010]中位数
就是让你要么插入一个数,要么输出当前序列的中位数,中位数是指将一个序列按照从小到大排序后处在中间位置的数。
我们首先将所有的数丢进大根堆里然后取一般丢进小根堆里,这样就把所有的数分成了两段有序的部分。
加入操作可以每次取小根堆堆顶和加入的数比较,大于堆顶则加入小根堆否则加入大根堆(为了维护这个排序的有序性)。
当询问时输出大根堆中的堆顶元素即可。
代码:
#include<bits/stdc++.h>
using namespace std;
priority_queue<int,vector<int>,greater<int> > xiao;
priority_queue<int> da;
string cz;
int n,m,aa,a;
int main() {
cin>>n;
for(int i=1; i<=n; i++) cin>>aa,da.push(aa);
for(int i=1; i<=(n>>1); i++) xiao.push(da.top()),da.pop();
cin>>m;
while(m--) {
cin>>cz;
if(cz=="add") {
cin>>a,n++;
if(a>da.top()) xiao.push(a);
else da.push(a);
} else {
while(da.size()<(n+1>>1)) da.push(xiao.top()),xiao.pop();
while(da.size()>(n+1>>1)) xiao.push(da.top()),da.pop();
cout<<da.top()<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号