手写一个堆:
1.插入一个数
2.求集合中的最小值
3.删除最小值
4.删除任意一个数
5.修改一个数
首先要明确:
1.堆是一个完全二叉树(除了最后一层节点从左到右不一定排满,其他层的结点一定全部排列)。
2.小根堆:每一个点存的值都比他的左右儿子小
3.大跟堆:每一个点存的值都比他的左右儿子大,与小根堆相反。
4.模拟堆用一个一维的数组p[x]来存储,p[x]表示在树的x位置存储的值。
5.存储方式:
例题
维护一个集合,初始时集合为空,支持如下几种操作:
I x
,插入一个数 x;PM
,输出当前集合中的最小值;DM
,删除当前集合中的最小值(数据保证此时的最小值唯一);D k
,删除第 k 个插入的数;C k x
,修改第 k 个插入的数,将其变为 x;
现在要进行 N 次操作,对于所有第 2 个操作,输出当前集合的最小值。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个操作指令,操作指令为 I x
,PM
,DM
,D k
或 C k x
中的一种。
输出格式
对于每个输出指令 PM
,输出一个结果,表示当前集合中的最小值。
每个结果占一行。
数据范围
1≤N≤1e5
−1e9≤x≤1e9
数据保证合法。
输入样例:
8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
输出样例:
-10
6
AC代码:
#include <iostream>
using namespace std;
const int N = 100010;
int n, h[N],mysize;
int ph[N],hp[N]; //ph是指向堆中的位置,ph[i]=j,表示第i个插入的数在堆中的位置为j
int k; //hp是堆中指向ph的数组,hp[j]=i,表示堆的j位置,对应第i个插入的数
void heap_swap(int a,int b) //堆交换
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
swap(h[a],h[b]);
}
void down(int u)
{
int t = u;
if(2 * u <= mysize && h[2 * u ] < h[t]) t = 2 * u; //与左二子比较
if(2 * u + 1 <=mysize && h[2 * u +1 ] < h[t]) t =2 * u + 1; //与右儿子比较
if(u!=t)
{
heap_swap(u,t);
down(t);
}
}
void up(int u)
{
int t = u;
if( u/2 >= 1 && h[u / 2] > h[t]) t= u/2;
if(u!=t)
{
heap_swap(t,u);
up(t);
}
}
int main()
{
cin>>n;
string op;
while(n--)
{
cin>>op;
if(op=="I")
{
int x;
cin>>x;
h[++mysize] = x;
ph[++k]=mysize,hp[mysize]=k;
up(mysize);
}
else if(op=="PM") cout<< h[1]<<endl; //输出最小的元素
else if(op=="DM")
{
heap_swap(1,mysize); //删除最小的元素
mysize--;
down(1);
}
else if(op=="D") //删除第k个插入的数
{
int k;
cin>>k;
k=ph[k];
heap_swap(k,mysize);
mysize--;
up(k);
down(k);
}
else //修改第k个插入的数
{
int k,x;
cin>>k>>x;
k=ph[k];
h[k]=x;
up(k);
down(k);
}
}
return 0;
}
——来源ACWing 基础算法 模拟堆