优先队列大家都听说过吧,而其实堆这个数据结构就可以用来实现优先队列。
例题
洛谷3378 堆
题目描述
请根据输入对小根堆进行处理。
输入格式
第一行包含一个整数N,表示操作的个数。
接下来N行,每行包含1个或2个正整数,表示三种操作,格式如下:
操作1: 1 x 表示将x插入到堆中。
操作2: 2 输出该小根堆内的最小数 。
操作3: 3 删除该小根堆内的最小数。
输出格式
包含若干行正整数,每行依次对应一个操作2的结果。
输入输出样例
输入
5
1 2
1 5
2
3
2
输出
2
5
说明/提示
数据规模:
对于30%的数据:N<=15。
对于70%的数据:N<=10000。
对于100%的数据:N<=1000000。
堆
堆这个数据结构你可以想象成一个完全二叉树的样子(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边),只不过这个完全二叉树满足所有的父亲节点都大于或小于它的子节点,通常直接以一维数组的方式进行存储。
而这里堆分为两类:最大堆和最小堆。顾名思义,最大堆就是一个所有父亲节点都大于它子节点的完全二叉树,而最小堆就是一个所有父亲节点都小于它子节点的完全二叉树。
这里举个例子,数组a = {0, 23, 18, 10, 11, 12, 5, 8, 5, 4, 12}(这里我是从第一位开始算的)。
我们可以看到这个二叉树就是一个最大堆。这也就保证了堆顶top,即a[1],是所有数中最大的。这之后我就以最大堆为例了。
对与堆这个数据结构,我们需要实现的方法有3个:
- push(x):将x插入到堆中。具体一些就是把这个数先放到队尾,之后比较这个数和其父节点的大小,如果这个数比其父节点大,就交换这个数和其父节点的值,然后再不停地去比较这个数和当前所在节点的父亲节点的大小,直到这个数小于当前所在节点的父亲节点或者到达堆顶。
- pop():删除最大的数,即堆顶。显然我们不可以直接删去这样堆的结构就被打乱了。那么我们需要把堆顶的值与最后一个节点(队尾)的值交换,之后删去队尾,再从堆顶开始每次选择当前所在节点的子节点中的最大点与当前所在节点进行比较,如果说前者大于后者则交换二者,并更新当前位置为子节点位置继续循环,否则就代表更新完毕。
- top():返回最大值,即堆顶元素,这里直接return a[1]就行了
但是务必注意这道题我们需要的是最小堆,所以直接把大于小于符号反过来就行了。
最后,算一下push和pop的时间复杂度:因为每次最多更新的次数就是这棵树的深度,所以我们发现时间复杂度是log2(n)级别的。
再补充一下,推荐用结构体封装,这样代码看起来会舒服的多。
代码
# include <cstdio>
# include <cmath>
# include <cstring>
# include <algorithm>
using namespace std;
struct MinHeap // 最大堆名字要改成MaxHeap
{
static const int MAX = 100000;
int len, num[MAX + 10];
MinHeap() // 最大堆构造函数要改成MaxHeap
{
len = 0;
memset(num, 0, sizeof(num));
}
int top()
{
return num[1];
}
void push(int x)
{
num[++len] = x;
for (int dad = len / 2, son = len; dad > 0; dad /= 2, son /= 2) {
if (num[dad] <= num[son]) break; // 最大堆要改成num[dad] >= num[son]
swap(num[dad], num[son]);
}
}
void pop()
{
swap(num[1], num[len--]);
for (int dad = 1, son = 2; son <= len; dad = son, son *= 2) {
if (son + 1 <= len && num[son] > num[son + 1]) son++; // 最大堆要改成num[son] < num[son + 1]
if (num[dad] <= num[son]) break; // 最大堆要改成num[dad] >= num[son]
swap(num[dad], num[son]);
}
}
};
MinHeap heap; // 定义最大堆对象时要改成MaxHeap
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int type;
scanf("%d", &type);
if (type == 1) {
int x;
scanf("%d", &x);
heap.push(x);
}
if (type == 2) printf("%d
", heap.top());
if (type == 3) heap.pop();
}
return 0;
}