【模板】堆【堆】
题目大意:
一个初始小根堆为空,我们需要支持以下3种操作:
- 1 x 表示将x插入到堆中
- 2 输出该小根堆内的最小数
- 3 删除该小根堆内的最小数
思路:
很简单,堆的模板题。维护一个小根堆即可。
堆是什么?
一般来说,堆是一个二叉树,它能维护一个数列的最大或最小值。
堆又分为小根堆和大根堆,小根堆是维护最小值的(即为最小值),而大根对是维护最大值的(即为最大值)。
时间复杂度:
- 建堆:
- 更改元素:
比如说,下图就是一个小根堆:
它符合一种特性:且
当我们插入一个元素时,堆会怎样维护最小值(最大值)呢?
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。
它就会和他的父亲节点比较,若它的父亲节点比他大,就交换两数的位置。
继续,和他的父亲节点比较,,交换。
这时再与它的父亲节点比较,由于,所以就不用再往上了,维护结束,依然保证且。
那么对应该如何删除最小值(最大值),但是依旧维护呢?
这里一般的方法是:将点(最小值或最大值)和点(最后一个点)交换,再将点下移,直到无法往下。
比如说,我们要删除点1(点权为3的点),就先将它和最后一个点交换
将最后一个点(点权最小的点)删除
将交换上来的点下移,注意要选择两个儿子节点中点权更小的点交换(如果是大根堆就要找点权更大的点)。
继续下移,,将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;
}