【模板】堆
题目描述
给定一个数列,初始为空,请支持下面三种操作:
- 给定一个整数 ,请将 加入到数列中。
- 输出数列中最小的数。
- 删除数列中最小的数(如果有多个数最小,只删除 个)。
输入格式
第一行是一个整数,表示操作的次数 。
接下来 行,每行表示一次操作。每行首先有一个整数 表示操作类型。
- 若 ,则后面有一个整数 ,表示要将 加入数列。
- 若 ,则表示要求输出数列中的最小数。
- 若 ,则表示删除数列中的最小数。如果有多个数最小,只删除 个。
输出格式
对于每个操作 ,输出一行一个整数表示答案。
样例 #1
样例输入 #1
5
1 2
1 5
2
3
2
样例输出 #1
2
5
提示
【数据规模与约定】
- 对于 的数据,保证 。
- 对于 的数据,保证 。
- 对于 的数据,保证 ,,。
思路
堆是一种特殊的完全二叉树,最小堆的特性是父节点的值小于或等于其子节点的值。这个最小堆支持三种操作:插入(push)、查看堆顶元素(堆中最小元素)和删除堆顶元素(pop)。
首先定义一个数组hmin来存储堆中的元素,cnt用来记录当前堆中的元素数量。然后,定义了三个函数:push、pop和main。
push函数用来向堆中插入元素。首先,将新元素插入到数组的最后,然后通过一系列的“上浮”操作,将这个新元素移到合适的位置,保证堆的性质不变。"上浮"操作是通过比较新插入的节点和其父节点的值,如果新节点的值小于父节点,则交换两者的位置,直到新节点的值不小于其父节点或者新节点成为了根节点。
pop函数用来删除堆顶元素。首先,交换堆顶元素和数组的最后一个元素,然后删除数组的最后一个元素(即原堆顶元素)。接着,通过一系列的“下沉”操作,将新的堆顶元素移动到合适的位置,保证堆的性质不变。"下沉"操作是通过比较父节点和其子节点的值,如果父节点的值大于子节点,则交换两者的位置,直到父节点的值不大于任何一个子节点或者父节点成为了叶子节点。
main函数首先读入一个整数n,表示接下来有n个操作。每个操作的类型由整数op决定:当op为1时,读入一个整数t,调用push函数将t插入堆中;当op为2时,输出堆顶元素;当op为3时,调用pop函数删除堆顶元素。
AC代码
#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
const int N = 1e7 + 7;
int n;
int hmin[N];
int cnt = 0;
void push(int x) {
hmin[++cnt] = x;
int p = cnt;
// 上浮
while (p) {
int parent = p >> 1;
if (hmin[parent] > hmin[p]) {
swap(hmin[parent], hmin[p]);
} else {
break;
}
p = parent;
}
return;
}
void pop() {
swap(hmin[1], hmin[cnt--]);
int p = 1;
// 下沉
while ((p << 1) <= cnt) {
int child = p << 1;
int childr = child + 1;
if (childr <= cnt && hmin[child] > hmin[childr]) {
// 右孩子更小
child = childr;
}
if (hmin[child] < hmin[p]) {
swap(hmin[child], hmin[p]);
} else {
break;
}
p = child;
}
return;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
int op;
int t;
cin >> op;
switch (op) {
case 1:
cin >> t;
push(t);
break;
case 2:
cout << hmin[1] << endl;
break;
case 3:
pop();
break;
}
}
return 0;
}