题目描述
给定一个数列,初始为空,请支持下面三种操作:
- 给定一个整数 x,请将 x 加入到数列中。
- 输出数列中最小的数。
- 删除数列中最小的数(如果有多个数最小,只删除 1 个)。
输入格式
第一行是一个整数,表示操作的次数 n。
接下来 nn 行,每行表示一次操作。每行首先有一个整数 op 表示操作类型。
- 若 op=1,则后面有一个整数 x,表示要将 x 加入数列。
- 若 op=2,则表示要求输出数列中的最小数。
- 若 op=3,则表示删除数列中的最小数。如果有多个数最小,只删除 1 个。
输出格式
对于每个操作 2,输出一行一个整数表示答案。
输入输出样例
输入 #1复制
5 1 2 1 5 2 3 2
输出 #1复制
2 5
说明/提示
【数据规模与约定】
- 对于 30% 的数据,保证 n≤15。
- 对于 70% 的数据,保证 n≤10^4。
- 对于 100% 的数据,保证1≤n≤10^6,1≤x<2^31,op∈{1,2,3}。
利用STL库里的优先队列实现堆 AC代码如下
#include<bits/stdc++.h>
using namespace std;
priority_queue<int> q;//定义降序优先队列
int n, a, b;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d", &a);
switch (a) {
case 1: {
scanf("%d", &b);
q.push(-b); //把-b压入优先队列 从而实现升序,即原来最小的,取相反数后成最大的,会放在队首
break;
}
case 2: {
int ans = q.top(); //获取队首值
printf("%d\n", -ans); //输出队首相反数,因为入队时是取相反数入队
break;
}
case 3: {
q.pop(); //将队首弹出
break;
}
}
}
return 0;
}
了解优先队列priority_queue用法 可戳以下链接
另外贴上手打堆代码,其实堆就是个完全二叉树,此处用到最小堆结构,只不过手打堆在此题倒数第二个测试点TLE了,虽然用的空间更小,但是其他的测试点的速度也不是非常快,总而言之还是优先队列好用。
#include<bits/stdc++.h>
using namespace std;
int n,p,k=0;
int h[1000010];
void my_swap(int x, int y) {//交换根结点和较小儿子的值
int t = h[x];//使原根结点和较小儿子的值交换
h[x] = h[y];
h[y] = t;
return;
}
void siftdown(int i) {
//使以i为根结点的树满足最小堆结构
if (i == 0)return;
int t, flag = 0;//flag用来标记是否需要继续向下调整
//t用来记录每次较小的标号
while (i * 2 <= k && flag == 0) {
if (h[i] > h[i * 2])//先判断和左儿子的关系
t = i * 2;
else t = i;
//如果它有右儿子,那就继续判断
if (i * 2 + 1 <= k) {
if (h[t] > h[i * 2 + 1])
t = i * 2 + 1;
}
if (t != i) {//说明需要调整
my_swap(t, i);
i = t;
}
else//否则此树已经符合最小堆结构
flag = 1;
}
return;
}
void creat() {
int i;
//对于有n个结点的完全二叉树来说n/2是最后一个非叶结点
//从此处开始逐个遍历以第i个结点为根结点的树
//使以为此结点为根结点的二叉树满足最小堆结构
for (int i = k / 2; i >= 1; i--)
siftdown(i);
}
void deletemin() {
int t = h[1];
h[1] = h[k];
k--;
siftdown(1);
return ;
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &p);
if (p == 1) {
scanf("%d", &h[++k]);
creat();//建最小堆
}
if (p == 2)printf("%d\n", h[1]);
if (p == 3) {
deletemin();
}
}
return 0;
}
最后贴上C语言手打堆AC代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#define LL long long
void min_down(int num[], int i, int size);
void swap(int *a, int *b);
void min_up(int num[], int i, int size);
void min_insert(int num[], int value, int *size);
void min_pop(int num[], int *size);
void min_rank(int num[], int size);
void min_pop(int num[], int *size);
void swap(int *a, int *b)
{
int c = *a;
*a = *b;
*b = c;
return;
}
void min_down(int num[], int i, int size) //最小堆向下更新
{
int child = i * 2;
while (child <= size)
{
if (child < size && num[child] > num[child + 1])
child++;
if (num[child] < num[i])
{
swap(&num[child], &num[i]);
i = child;
child = i * 2;
}
else
break;
}
return;
}
void min_up(int num[], int i, int size) //最小堆向上更新
{
while (i > 1)
{
if (num[i] < num[i / 2])
{
swap(&num[i], &num[i / 2]);
i /= 2;
}
else
break;
}
return;
}
void min_insert(int num[], int value, int *size) //最小堆插入
{
num[++(*size)] = value;
min_up(num, *size, *size);
}
void min_pop(int num[], int *size) //最小堆弹出堆顶
{
num[1] = num[(*size)--];
min_down(num, 1, *size);
}
void min_rank(int num[], int size) //堆排序 自大到小
{
for (int i = size; i >= 2;)
{
swap(&num[1], &num[i--]);
min_down(num, 1, i);
}
return;
}
void min_bulid(int num[], int size)
{
for (int i = size / 2; i >= 1; i--)
min_down(num, i, size);
}
int num[1000010];
int main()
{
int n;
scanf("%d", &n);
int size = 0;
while (n--)
{
int op;
scanf("%d", &op);
if (op == 1)
{
int x;
scanf("%d", &x);
min_insert(num, x, &size);
}
else if (op == 2)
printf("%d\n", num[1]);
else
min_pop(num, &size);
}
return 0;
}