题目描述
给定一个数列,初始为空,请支持下面三种操作:
1. 给定一个整数x,请将x加入到数列中。
2. 输出数列中最小的数。
3. 删除数列中最小的数(如果有多个数最小,只删除1个)。
输入格式
第一行是一个整数,表示操作的次数 n。
接下来 n 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。
- 若 op = 1,则后面有一个整数 x,表示要将 x 加入数列。
- 若 op = 2,则表示要求输出数列中的最小数。
- 若 op = 3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。
输出格式
对于每个操作2,输出一行一个整数表示答案。
样例 #1
样例输入 #1
5
1 2
1 5
2
3
2
样例输出 #1
2
5
提示
【数据规模与约定】
- 对于30%的数据,保证n ≤ 15。
- 对于70%的数据,保证n ≤ 。
- 对于100%的数据,保证1≤n≤,1≤x < ,op ∈ {1, 2, 3}。
分析:
这道题我们要知道堆的概念。
堆,其实就是一棵完全二叉树,如果你不理解,不妨看看百度
堆(Heap)是计算机科学中一类特殊的数据结构,是最高效的优先级队列。堆通常是一个可以被看作一棵完全二叉树的数组对象。
-
堆中某个结点的值总是不大于或不小于其父结点的值;
-
堆总是一棵完全二叉树。
-----百度百科
某个结点的值不大于其父结点的值被称为大根堆,如果不小于就称之为小根堆。
这下知道堆了吧 。
然后就是让我们执行3种操作:
1.往堆里加入元素
2.输出堆里最小的元素
3.删除最小的元素
第2种操作很简单,我就不讲了,我们就讲讲第1和第3个操作。
1.往堆里加入元素
这个其实没什么好讲的,见代码:
void push(int x)
{
a[++n]=x;
int u=n;
while(u/2>0)
{
if(a[u/2]<x) break;
swap(a[u],a[u/2]);
u/=2;
}
}
2.删除最小的元素
这个操作就是找最小的数,要注意要判断是否有左子树和右子树
void pop()
{
a[1]=a[n--];
int u=1,t;
while(1)
{
if(2*u<=n&&2*u+1<=n)
{
if(a[2*u]<a[2*u+1])
t=2*u;
else
t=2*u+1;
}
else if(2*u<=n&&2*u+1>n)
t=2*u;
else break;
if(a[u]<a[t]) break;
else
{
swap(a[u],a[t]);
u=t;
}
}
}
怎么样,会写代码了吧!
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m,a[N],op,x;
void push(int x)
{
a[++n]=x;
int u=n;
while(u/2>0)
{
if(a[u/2]<x) break;
swap(a[u],a[u/2]);
u/=2;
}
}
void pop()
{
a[1]=a[n--];
int u=1,t;
while(1)
{
if(2*u<=n&&2*u+1<=n)
{
if(a[2*u]<a[2*u+1])
t=2*u;
else
t=2*u+1;
}
else if(2*u<=n&&2*u+1>n)
t=2*u;
else break;
if(a[u]<a[t]) break;
else
{
swap(a[u],a[t]);
u=t;
}
}
}
int main()
{
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>op;
if(op==1)
{
cin>>x;
push(x);
}
else if(op==2)
{
cout<<a[1]<<endl;
}
else if(op==3)
{
pop();
}
}
return 0;
}
如果有问题可以在评论区讨论!!
题目来源:洛谷P3378 【模板】堆,AC代码