【模板】堆 (C++)

题目描述

给定一个数列,初始为空,请支持下面三种操作:

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 ≤ 10^{4}
- 对于100%的数据,保证1≤n≤10^{6},1≤x < 2^{31},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代码

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值