堆是一颗完全二叉树,其中的每个点都小于等于它的左右儿子,根节点总是当前集合中的最小值
手写堆可以实现的基本操作:
1、插入一个数 (heap[++size]=x, up(size))
2、求最小值 (heap[1])
3、删除当前最小值 (heap[1]=heap[size], size–, down(1))
4、删除任意一个元素 (heap[k]=heap[size], size–, down(k), up(k))
5、修改任意一个元素 (heap[k]=x, down(k), up(k))
堆的存储:
堆用一维数组实现存储,其下标从1开始,并且1是根节点。对于节点x,它的左儿子是2x,右儿子是2x+1
实现堆的两个主要函数是down()和up();
顾名思义,down是将一个数向下调整,up是将一个数向下调整。对于一个数x,如果它比较大,就可以down(x),如果比较小就可以up(x)
AcWing 838. 堆排序
输入一个长度为n的整数数列,从小到大输出前m小的数。
输入格式
第一行包含整数n和m。
第二行包含n个整数,表示整数数列。
输出格式
共一行,包含m个整数,表示整数数列中前m小的数。
数据范围 1≤m≤n≤1e5, 1≤数列中元素≤1e9
//这题只用到了down操作
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+5;
int heap[N],sizes;
void heap_swap(int a,int b)
{
// swap(pu[hp[a]],ph[hp[b]]);
// swap(hp[a],hp[b]);
swap(heap[a],heap[b]);
}
void down(int u)
{
int t=u;
if(u*2<=sizes && heap[t]>heap[u*2]) t=u*2;
if(u*2+1<=sizes && heap[t]>heap[u*2+1]) t=u*2+1;
if(u!=t)
{
heap_swap(u,t);
down(t);
}
}
void up(int u)
{
while(u/2 && heap[u]<heap[u/2])
{
heap_swap(u,u/2);
u>>=1;
}
}
int main()
{
int n,m; cin>>n>>m;
for(int i=1;i<=n;i++) cin>>heap[i];
sizes=n;
for(int i=n/2;i;i--) down(i); //建堆
while(m--)
{
cout<<heap[1]<<" "; //输出当前最小值
heap[1]=heap[sizes--]; //删除当前最小值,用最后一个数覆盖掉第一个数,然后将最后一个数删除掉,在吧heap[1]中放的数down到合适的位置
down(1);
}
puts("");
return 0;
}
AcWing 839. 模拟堆
维护一个集合,初始时集合为空,支持如下几种操作:
“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
数据保证合法。
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#define ll long long
#define ull unsigned long long
#define up_b upper_bound
#define low_b lower_bound
#define m_p make_pair
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define inf 0x3f3f3f3f
#include<algorithm>
using namespace std;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') f=-1; ch=getchar(); }
while('0'<=ch&&ch<='9') x=x*10+ch-'0', ch=getchar();
return f*x;
}
const int N = 1e5+5;
int heap[N],ph[N],hp[N],sizes;
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 down(int u)
{
int t=u;
if(u*2<=sizes && heap[t]>heap[u*2]) t=u*2;
if(u*2+1<=sizes && heap[t]>heap[u*2+1]) t=u*2+1;
if(u!=t)
{
heap_swap(u,t);
down(t);
}
}
void up(int u)
{
while(u/2 && heap[u]<heap[u/2])
{
heap_swap(u,u/2);
u>>=1;
}
}
int main()
{
int n; cin>>n;
string op; int id=0,x,k;
while(n--)
{
cin>>op;
if(op=="I") //插入一个数x
{
cin>>x;
sizes++; id++;
heap[sizes]=x;
ph[id]=sizes, hp[sizes]=id;
//第id个插入的数在堆中的下标是sizes,在堆在下标是sizes的数是第id个插入的数
up(sizes);
}
else if(op=="PM") //输出当前最小值
cout<<heap[1]<<endl;
else if(op=="DM") //删除当前最小值
{
heap_swap(1,sizes); //将第一个与最后一个交换
sizes--; //删掉最后一个
down(1);
}
else if(op=="D") //删除第k个插入的数
{
cin>>k;
k=ph[k]; //找到第k个插入的数在heap中的下标
heap_swap(k,sizes);
sizes--;
up(k); down(k);
}
else //将第k个插入的数改为x
{
cin>>k>>x;
k=ph[k];
heap[k]=x;
up(k); down(k);
}
}
return 0;
}