模拟堆
数组建树:
//建树时间复杂度O(n)
void buildtree(int n)
{
for(int i=1;i<=n;i++) cin>>h[i];
for(int i=n/2;i;i--) down(i);
}
删除和插入都记得改变cnt!
题目:
https://www.acwing.com/problem/content/841/
heap_swap原理图解:
其中删除第k个数的错误写法:
void del_k(int k)
{
heap_swap(ph[k],cnt);
cnt--;
down(ph[k]);
up(ph[k]);
}
错误原因:heap_swap\down\up 的过程中 ph[k] 的值会发生变化
正确写法:down操作和up操作都应该针对堆下标为t的点
//删除第k个插入的数
void del_k(int k)
{
int t=ph[k];
heap_swap(t,cnt);
cnt--;
down(t);
up(t);
}
AC代码:
//最小堆
#include<bits/stdc++.h>
using namespace std;
const int maxn=100010;
int h[maxn];
int cnt=0;//heap范围为1~cnt
int m=0;//代表第几个插入的数
int ph[maxn];//ph[k]存储的是输入的第k个数的堆下标
int hp[maxn];//hp[k]存储的是堆下标为k的数对应的是第几个输入的数
void heap_swap(int a,int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
//down操作
//某个结点变大后,结点需要往下移动,与孩子中最小的那个交换
void down(int u)
{
int t=u;
if(u*2<=cnt && h[u*2]<h[t]) t=u*2;
if(u*2+1<=cnt && h[u*2+1]<h[t]) t=u*2+1;
if(u!=t)
{
//swap(h[u],h[t]);
heap_swap(u,t);
down(t);
}
}
//up操作
//当某个结点变小后,与父节点交换即可
void up(int u)
{
while( u/2 && h[u/2] > h[u])
{
//swap(h[u],h[u/2]);
heap_swap(u,u/2);
u/=2;
}
}
//输出最小值
void pickmin()
{
cout<<h[1]<<endl;
}
//删除最小值
void delmin()
{
//swap(h[1],h[cnt]);
heap_swap(1,cnt);
cnt--;//别漏
down(1);
}
//插入一个数
//插入到堆尾再进行向上调整
void insert(int x)
{
cnt++;
m++;
ph[m]=cnt;
hp[cnt]=m;
h[cnt]=x;
up(cnt);
}
//删除第k个插入的数
void del_k(int k)
{
int t=ph[k];
heap_swap(t,cnt);
cnt--;
down(t);
up(t);
}
//修改第k个数
void change(int k,int x)
{
int t=ph[k];
h[ t ]=x;
down(t);
up(t);
}
int main()
{
int n;
cin>>n;
while(n--)
{
char op[10];
scanf("%s",op);
if(!strcmp(op,"I"))
{
int x;
scanf("%d",&x);
insert(x);
}
else if(!strcmp(op,"PM"))
{
pickmin();
}
else if(!strcmp(op,"DM"))
{
delmin();
}
else if(!strcmp(op,"D"))
{
int k;
scanf("%d",&k);
del_k(k);
}
else
{
int k,x;
scanf("%d%d",&k,&x);
change(k,x);
}
}
return 0;
}