数组模拟堆 模板

76 阅读2分钟

image.png

如何手写一个堆?

我们对堆主要有如下操作:

一.插入一个数

我们首先把要插入的数x插入到堆的末尾,再根据其值大小进行是否up()操作:

heap[++size]=x; up(size);

二.求集合当中的最小值

对小堆而言,堆顶元素就是最小值

heap[1]

三.删除最小值

因为是用一维数组模拟堆,数组开头元素是最难删除的,需要后面的元素一阶一阶向前覆盖。最组尾元素是最容易删除的,只有size--就可以了。

因此我们只要把堆尾的元素覆盖给堆顶元素,然后再把堆尾元素删除了,最后再对堆顶元素进行down()操作

heap[1]=heap[size]; 
size--;
down(1);

四.删除任一元素

对于任一元素x我们先找到它,然后用堆尾元素覆盖x,再把堆尾元素减去,再根据heap[x]的值进行up()或down()操作

heap[k]=heap[size]
size--;
down()||up();

五.修改任一元素

对于任一元素x,我们先找到它,然后将其修改,修改完之后再根据其修改后的值的大小进行up()或down()操作

heap[k]=x;
down()||up()

模板题

838. 堆排序 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int h[N],sz;




void down(int u)
{
    int t=u;  //我们用t来存最小值的下标
    
    if(u*2<=sz&&h[u*2]<h[t])t=u*2;
    if(u*2+1<=sz&&h[u*2+1]<h[t])t=u*2+1;
    
    if(t!=u)  //说明u不是最小值
    {
        swap(h[u],h[t]);
        down(t);
    }
}

void up(int u)
{
    while(u/2&&h[u/2]>h[u]){  //当父节点大于u节点的时候
        swap(h[u/2],h[u]);    //就把较大的父节点换下来,把较小的u节点送上去
        u/=2;                 //继续向上找父亲节点
    }
}
signed main()
{
    
    int n,m;cin>>n>>m;
    sz=n;
    
    for (int i = 1; i <= n; i++)
        scanf("%d", &h[i]);
    for (int i = n / 2; i; i--)
        down(i);
    
    
    while(m--)
    {
        printf("%d ",h[1]);
        h[1]=h[sz];
        sz--;
        down(1);
    }
    
    return 0;
}