【模板】堆【堆】

题目大意:

一个初始小根堆为空,我们需要支持以下3种操作:

  1. 1 x 表示将x插入到堆中
  2. 2 输出该小根堆内的最小数
  3. 3 删除该小根堆内的最小数

思路:

很简单,堆的模板题。维护一个小根堆即可。

堆是什么?

一般来说,堆是一个二叉树,它能维护一个数列的最大或最小值。
堆又分为小根堆和大根堆,小根堆是维护最小值的(即 a [ 1 ] 为最小值),而大根对是维护最大值的(即 a [ 1 ] 为最大值)。
时间复杂度:

  • 建堆: O ( n )
  • 更改元素: O ( n l o g n )

比如说,下图就是一个小根堆:
这里写图片描述
它符合一种特性: a [ x ] a [ x × 2 ] a [ x ] a [ x × 2 + 1 ]
当我们插入一个元素时,堆会怎样维护最小值(最大值)呢?

void up(int x)
{
    a[++k]=x;
    int y=k;
    while(y>1&&a[y]<a[y/2])
    {
        swap(a[y],a[y/2]);
        y/=2;
    }
}

假设我们又插入了一个元素4。
这里写图片描述
它就会和他的父亲节点比较,若它的父亲节点比他大,就交换两数的位置。
这里写图片描述
继续,和他的父亲节点比较, 6 > 4 ,交换。
这里写图片描述
这时再与它的父亲节点比较,由于 3 < 4 ,所以就不用再往上了,维护结束,依然保证 a [ x ] a [ x × 2 ] a [ x ] a [ x × 2 + 1 ]
那么对应该如何删除最小值(最大值),但是依旧维护呢?
这里一般的方法是:将点 1 (最小值或最大值)和点 k (最后一个点)交换,再将点 k 下移,直到无法往下。
比如说,我们要删除点1(点权为3的点),就先将它和最后一个点交换
这里写图片描述
将最后一个点(点权最小的点)删除
这里写图片描述
将交换上来的点下移,注意要选择两个儿子节点中点权更小的点交换(如果是大根堆就要找点权更大的点)
这里写图片描述
继续下移, 6 < 13 < 18 ,将18和6交换。
这里写图片描述
由于没有子节点了,下移结束。
所以,运用好这两个方法,就能很好的完成堆的题目。


代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

int n,m,x,a[5000001],k;

void up(int x)  //上移
{
    a[++k]=x;
    int y=k;
    while(y>1&&a[y]<a[y/2])
    {
        swap(a[y],a[y/2]);
        y/=2;
    }
}

void down()  //下移
{
    swap(a[1],a[k]);
    k--;
    int x=1;
    int y=2;
    while ((y<=k&&a[x]>a[y])||(y+1<=k&&a[x]>a[y+1]))
    {
        if (a[y]>a[y+1]&&y+1<=k)  y++;  //选择更小的那一个
        swap(a[x],a[y]);
        x=y;
        y=2*x;
    }
}

int main()
{
    scanf("%d",&n);
    while (n--)
    {
        scanf("%d",&m);
        if (m==1)  //操作1
        {
            scanf("%d",&x);
            up(x);
        }
        if (m==2)  //操作2
        {
            printf("%d\n",a[1]);
        }
        if (m==3)  //操作3
        {
            down();
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ssl_zyc/article/details/81048036