小根堆就是我们数据结构里面常常提到的那个堆,一个堆是由一个完全二叉树所构成的。小根堆为什么叫小根堆呢,因为它的根节点永远是比其儿子节点小的。正是由于这个性质,所以我们能有 堆排序这种东西。
这里我们用一个数组来模拟我们的堆,如下图所示
父节点的下标为n,其子节点分别为2n以及2n+1。堆的维护,需要两个操作一个是up一个是down,也就是调整数在堆中的位置,使其按照小根堆的父节点一定大于子节点的样子。
void up(int u)
{
int t=u;
if(u/2&&q[u/2]>q[u])
{
t=u/2;
good_swap(u,t);//一个交换函数下面会讲到为什么不直接用swap
up(t);//递归调用看看换完之后是否还能接着往上走
}
}
void down(int x)
{
int t =x;
if(2*x<=cnt&&q[t]>q[2*x])t=2*x;
if(2*x+1<=cnt&&q[t]>q[2*x+1])t=2*x+1;//cnt是堆里面数的个数,也就是其子节点必须得存在
if(t!=x)
{
good_swap(t,x);
down(t);
}
}
首先是插入操作,用数组模拟堆我一般习惯从下标1开始存储,每次插入先把数插入到堆的最后,然后再给它up一遍即可,也就是q[++cnt]=x;up(cnt);
然后是输出最小值,也就是我们的堆顶,由于只是输出所以cout<<q[1]<<endl;
删除最小值就是,把堆里面存储在最后面的数,和堆顶的数互换,然后再down一下堆顶即可。
但是得记得要cnt--;不然的话就相当于没删除了,堆顶会在up的时候重新回到堆顶,因为后面插入的数不会覆盖掉最后原来的那个cnt而是被赋值给了cnt+1;
因为题目有个操作是要删除和修改第k个插入的数,那如果我们直接用swap函数,交换之后,去哪里找我们的第k个插入的数字呢。
这里 我们需要一组映射关系利用一个
#include<iostream>
#include<string.h>
using namespace std;
const int N=1e5+10;
int ph[N],hp[N],q[N],cnt,s;//q是堆,cnt是堆的长度,s是记录插入到了第几个数
void good_swap(int a,int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(q[a],q[b]);
}
void up(int u)
{
int t=u;
if(u/2&&q[u/2]>q[u])
{
t=u/2;
good_swap(u,t);
up(t);
}
}
void down(int u)
{
int t=u;
if(u*2<=cnt&&q[u*2]<q[t])t=u*2;
if(u*2+1<=cnt&&q[u*2+1]<q[t])t=u*2+1;
if(t!=u)
{
good_swap(t,u);
down(t);
}
}
int main()
{
int m;
scanf("%d,",&m);
while (m -- ){
char op[5];
int d,k;
scanf("%s",op);
if(!strcmp(op,"I"))
{
scanf("%d",&d);
q[++cnt]=d;
s++;
ph[s]=cnt;
hp[cnt]=s;
up(cnt);
}
else if(!strcmp(op,"PM"))
{
printf("%d\n",q[1]);
}
else if(!strcmp(op,"DM"))
{
good_swap(1,cnt);
cnt--;
down(1);
}
else if(!strcmp(op,"D"))
{
scanf("%d",&k);
int u=ph[k];
good_swap(u,cnt);
cnt--;
up(u);
down(u);
}
else{
scanf("%d%d",&k,&d);
int u=ph[k];
q[u]=d;
up(u);
down(u);
}
}
return 0;
}
的堆中,另外还需一个hp[i]=k;表示第i下标存的是第k个数,这俩数组是相互映射的关系
所以交换函数的话ph,hp以及q都要交换
void good_swap(int x,int y)
{
swap(ph[hp[x]],ph[hp[y]]);
swap(hp[x],hp[y]);
swap(q[x],q[y]);
}
接下来就是全部的代码