Luogu P3378 【模板】堆
堆是一个完全二叉树,对于小根堆,所有父节点<=子节点,下标就和线段树是一样的
在STL里就是优先队列
只有堆顶元素可以操作(询问或弹出)。
加入新元素时x,heap[++size] = x,下标t=size;
每次比较它和父节点(t/2)的大小,如果它较小就swap。
删除堆顶元素时,用最后一个元素heap[size--]覆盖heap[1],下标t=1;
每次比较它和左右两个儿子的大小,和较小的swap。
注意:
- 下标t的值不能大于堆的size;
- 我一开始是这么写的:
-
void del() { swap(heap[1],heap[cnt--]); int t = 1; while(t*2 <= cnt && (heap[t] > heap[t*2] || heap[t] > heap[t*2+1])) { if(heap[t*2+1] < heap[t*2] && t*2+1 <= cnt) { swap(heap[t],heap[t*2+1]); t = t*2+1; } else { swap(heap[t],heap[t*2]); t *= 2; } } }
把swap(heap[1],heap[cnt--])改为heap[1] = heap[cnt--]就对了
- 如果把最小的数x放到了下面,将最后一个数y从堆顶下移,判断到x的父亲时,一定有x<=y。因为判断是否交换时的条件是左右儿子有一个>y就继续循环,虽然此时判断x的下标>size,但是会默认和另一个儿子交换。
- 如果最后一个值是空的时(为0)就更会翻车了!所以边界条件还是分别判断,这样swap也没问题啦。
代码如下
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn = 1000005;
int heap[maxn],n,a,b,cnt;
void insert(int x) {
heap[++cnt] = x;
int t = cnt;
while(heap[t] < heap[t/2]) {
swap(heap[t],heap[t/2]);
t/=2;
}
}
void del() {
heap[1] = heap[cnt--];
int t = 1;
while((t*2 <= cnt && heap[t] > heap[t*2]) || (t*2+1 <= cnt && heap[t] > heap[t*2+1])) {
if(heap[t*2+1] < heap[t*2] && t*2+1 <= cnt) {
swap(heap[t],heap[t*2+1]);
t = t*2+1;
} else {
swap(heap[t],heap[t*2]);
t *= 2;
}
}
}
int main() {
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%d",&a);
if(a == 1) {
scanf("%d",&b);
insert(b);
}
if(a == 2) {
if(cnt)printf("%d\n",heap[1]);
}
if(a == 3) {
del();
}
}
return 0;
}