维护一个集合,初始时集合为空,支持如下几种操作:
I x
,插入一个数 x;PM
,输出当前集合中的最小值;DM
,删除当前集合中的最小值(数据保证此时的最小值唯一);D k
,删除第 k 个插入的数;C k x
,修改第 k个插入的数,将其变为 x;
现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小值。
输入样例:
8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
输出样例:
-10
6
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
int n,m,Size;
int heap[N],ph[N],hp[N]; //ph指p->h,hp指h->p的指针
void heap_swap(int a,int b){
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(heap[a],heap[b]);
}
void up(int t){
while(t/2 && heap[t/2]>heap[t]){
heap_swap(t/2,t);
t/=2;
}
}
//利用int类除法的性质,可以不用分奇偶实现up
// if(t==1) return ;
// int idx;
// if(t%2) idx=(t-1)/2;
// else idx=t/2;
// if(heap[idx]>heap[t]){
// swap(heap[idx],heap[t]); //加入映射关系后不只要交换heap中的值
// up(idx); //up实现较简单,无需递归实现,可以优化掉
// }
void down(int t){
int idx=t;
if( t*2 <= Size && heap[t*2] < heap[idx] ) idx=2*t;
if( t*2+1 <= Size && heap[t*2+1] < heap[idx] ) idx=2*t+1;
//如果左右子叶都大于该节点,idx更新为其中任一的下标都行,第二句判断之前用else if在数据较多时会wrong
if(idx!=t){
heap_swap(idx,t);
down(idx);
}
}
int main()
{
m=0,Size=0;
int k,x;
cin>>n;
char op[5];
while(n--){
scanf("%s",op);
if(!strcmp(op,"I")){
cin>>x;
m++;
heap[++Size]=x;
ph[m]=Size,hp[Size]=m;
up(Size);
}
else if(!strcmp(op,"PM"))
cout<<heap[1]<<'\n';
else if(!strcmp(op,"DM")){
heap_swap(1,Size);
Size--;
down(1);
}
else if(!strcmp(op,"D")){
cin>>k;
k=ph[k]; //通过统计次序的数组找到,转换成在heap中对应的位置
heap_swap(k,Size);
Size--;
down(k),up(k);
}
else{ //'C'
cin>>k>>x;
k=ph[k];
heap[k]=x;
down(k),up(k);
}
}
return 0;
}
难点:随机的修改和删除,需要双向映射