世界上目前有很多种堆,比如二叉堆、斐波那契堆、配对堆、左 偏树等等,不过由于各种原因,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;
}

 

posted @ 2022-07-26 22:21  唯私の超电磁砲  阅读(47)  评论(0编辑  收藏  举报