欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【模板】堆【堆】

程序员文章站 2022-06-11 12:18:16
...

题目大意:

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

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

思路:

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

堆是什么?

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

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

比如说,下图就是一个小根堆:
【模板】堆【堆】
它符合一种特性: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;
}
相关标签: 洛谷 模板题