Luogu P3378 【模板】堆

((^ 0.0 ^)    )~

堆是一个完全二叉树,对于小根堆,所有父节点<=子节点,下标就和线段树是一样的

在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
",heap[1]);
        }
        if(a == 3) {
            del();
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/mogeko/p/10297024.html