传送门
题目描述
如题,初始小根堆为空,我们需要支持以下3种操作:
操作1: 1 x 表示将x插入到堆中
操作2: 2 输出该小根堆内的最小数
操作3: 3 删除该小根堆内的最小数
输入输出格式
输入格式:第一行包含一个整数N,表示操作的个数
接下来N行,每行包含1个或2个正整数,表示三种操作,格式如下:
操作1: 1 x
操作2: 2
操作3: 3
包含若干行正整数,每行依次对应一个操作2的结果。
输入输出样例
输入样例:
5
1 2
1 5
2
3
2
输出样例:
2
5
说明
时空限制:1000 ms , 128 M
数据规模:
对于30%的数据:N<=15
对于70%的数据:N<=10000
对于100%的数据:N<=1000000(注意是6个0。。。不过不要害怕,经过编者实测,堆是可以AC的)
样例说明:
故输出为2、5
题解
堆(也叫优先队列)是一棵完全二叉树,堆中的某个节点的值总是不大于或不小于它的父节点的值。前者叫做大根堆,后者叫做小根堆。
上图一个为大根堆,保证每一个节点的值都小于或等于它父节点的值。
本题的操作为维护一个小根堆。
在一个堆中,若一个非叶子结点的编号为 x,则它的左儿子的编号为 x*2,若他有右儿子,则它的右儿子的编号为 x*2+1。若一个非根节点的编号为x,则它的父节点的编号为 x/2。
维护堆有两种操作:
(1).从根节点开始向下维护
找到父节点(x)的值与它的一个(x*2)或两个儿子(x*2 与 x*2+1)的值中最小的一个,如父节点的值不是最小的,则交换,反之则维护完成。
(2).从叶子结点开始向上维护
若一个节点(x)的值小于它的父节点(x/2)的值,则交换,反之则维护完成。
1.插入
往堆中插入一个数 x。
我们先把 x 放到堆的末尾,然后向上维护小根堆。
2.输出该小根堆内的最小数
输出根节点。
3.删除
把堆的末尾的值放到根节点,向下维护小根堆。
Code:
#include<cstdio>
#include<cstdlib>
int a[1000010];
int n,m;
void su(int x)
{
while(x/2>0 && a[x]<a[x/2])
{
int t=a[x];a[x]=a[x/2];a[x/2]=t;
x=x/2;
}
}
void sd(int x)
{
int t;
while(x*2<=m)
{
if(a[x]>a[x*2]) t=x*2;else t=x;
if(x*2+1<=m && a[t]>a[x*2+1]) t=x*2+1;
if(t!=x) {int tt=a[x];a[x]=a[t];a[t]=tt;x=t;}
else break;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int p;
scanf("%d",&p);
if(p==1)
{
int x;
scanf("%d",&x);
m++;a[m]=x;
su(m);
}
if(p==2) printf("%d\n",a[1]);
if(p==3)
{
a[1]=a[m];m--;
sd(1);
}
}
}