【模板】堆【堆】

题目大意:

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

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

思路:

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

堆是什么?

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

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

比如说,下图就是一个小根堆:
这里写图片描述
它符合一种特性: a[x]a[x×2] a [ x ] ≤ a [ x × 2 ] a[x]a[x×2+1] 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 6 > 4 ,交换。
这里写图片描述
这时再与它的父亲节点比较,由于 3<4 3 < 4 ,所以就不用再往上了,维护结束,依然保证 a[x]a[x×2] a [ x ] ≤ a [ x × 2 ] a[x]a[x×2+1] a [ x ] ≤ a [ x × 2 + 1 ]
那么对应该如何删除最小值(最大值),但是依旧维护呢?
这里一般的方法是:将点 1 1 (最小值或最大值)和点k(最后一个点)交换,再将点 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;
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值