【题解】洛谷P3378 堆(模板)

正式比赛时我们肯定不愿意手写堆,不过作为一道模板题,我们还是手写一下并体会堆的过程比较好。

小根堆的存在形式是一棵二叉树,根节点是最小的数。我们初始化树上所有节点的权值为INF。当要添加一个数x时,我们把当前编号+1,把数放在那儿,并记录下当前编号。然后不断将其与其父亲结点进行比较,如果小就交换,并让记录下来的该数的编号除以2。(对于二叉树来说 父亲节点编号为x,那么它两个儿子节点编号必然是x*2与x*2+1,代表左儿子与右儿子)。这样如果要找最小元素,只需输出根节点就行了,时间复杂度O(1)。如果要删除最小值,那我们记录根节点编号为1,并不断将其与其子节点比较,如果左比右大就让左代替,并把编号*2,反之右大右代替,编号*2+1。最后把根节点对应的编号变成INF。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1000010;
const int INF=0x3f3f3f3f;
int num[maxn*8];
int tot=0;
void add(int x)
{
	tot++;
	num[tot]=x;
	int q=tot;
	while(num[q]<num[q/2]&&q!=1)
	{
		swap(num[q],num[q/2]);
		q=q/2;
	}
}
int top()
{
	return num[1];
}
void pop()
{
	int q=1;
	while(num[q*2]!=INF||num[q*2+1]!=INF)
	{
		if(num[q*2]<num[q*2+1]) 
		{
			num[q]=num[q*2];
			q=q*2;
		}
		else
		{
			num[q]=num[q*2+1];
			q=q*2+1;
		}
	}
	num[q]=INF;
	return ;
}
int main()
{
	memset(num,INF,sizeof(num));
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(x==1)
		{
			int q;
			scanf("%d",&q);
			add(q);
		}
		if(x==2)
		{
			printf("%d\n",top());
		}
		if(x==3)
		{
			pop();
		}
	}
	return 0;
}

但堆其实和快排一样,在c++里是可以直接调用的。这里要用到头文件#include<queue>,然后记住int类型的小根堆的表示形式为priority_queue<int,vector<int>,greater<int> >  q;,大根堆的表现形式为priority_queue<int,vector<int>,less<int> > p;。这个题就可以很轻松地解决了。如果要对某个结构体进行堆操作,定义结构体后可以用priority_queue<R> q; 的方式定义堆(R为结构体名称),注意这里假如要比较小根堆需要重载运算符,具体操作按题目的要求。返回值应该是大于号(我也不知道为啥,自己大于小于都试试能对就行)。

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstdio>
using namespace std; 
priority_queue<int,vector<int>,greater<int> >   q;
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		if(x==1) {scanf("%d",&x);q.push(x);}
		else if(x==2) printf("%d\n",q.top());
		else q.pop();       
	}   
} 

猜你喜欢

转载自blog.csdn.net/Rem_Inory/article/details/81489275