题目描述 题目链接
题目分析
1.堆的操作只需要 down up swap 就可以实现
2.堆其实就是一个完全二叉数,分为小堆,大堆,他的性质是每一个节点u 的左儿子是2u,右儿子是2u+1;
3.小堆的父节点小于他的儿子,大堆的父节点大于他的儿子
4.小堆的操作中 down 的操作:
使用递归的方式进行down
具体代码:(小根堆)
void down(int u) //需要down的根节点
{
int t=u; //t用来存储u的最小儿子
if(2*u<=len && h[u*2]<h[t]) t=u*2;
if(2*u+1<=len && h[u*2+1]<h[t]) t=u*2+1;
if(t!=u) //如果t不相等意味着u还不满足小堆的条件于最小儿子交 换后继续down
{
h_swap(t,u); //当有映射关系时需要用这个自定义函数
down(t); //递归
}
}
5.小堆操作中 up 的操作:
同样使用递归或者使用y总的循环
具体代码:(递归小根堆)
void up(int u)
{
int t=u; //up中的t保存的是父结点
if(u/2 && h[u/2]>h[t]) t=u/2; //up操作中只需要判断up儿子与根的大小就可
if(t!=u) //递归操作
{
h_swap(t,u);
up(t);
}
}
具体代码:(y总循环)
void up(int u)
{
while(u/2 && h[u/2]>h[u])
{
h_swap(u/2,u);
u/=2;
}
}
6.小堆中用up 和 down 完成插入 与删除操作
基本思想:
insert:将插入的元素放入最小根的底端然后进行up操作
delt:将删除元素t 与 最后一个元素交换 然后down(t)(这里的t是交换后最后一共元素现在所处的编号)len--
void insert(int u)
{
h[++len]=u;
up(len);
}
void delt(int k)
{
h[k]=h[len--];
down(k);
}
以上是常见堆的insert和delt
在题目模拟队中insert 和 delt的细节操作
让道古纠结了好久wa了一页(悲伤)
我在代码中独立出来了一定要看,就是下面
模拟堆的delt操作,那真是醉了
//先看8
下面是模拟堆insert和delt的操作:
void insert(int u);
{
h[++len]=u;
pk[len]=++idx;
kp[idx]=len;
up(len);
}
void delt(int k)
{
int u=kp[k];
h_swap(kp[k],len--);
up(u);
down(u);
}
7.模拟堆中h_swap
具体代码+注释
void h_swap(int a,int b)
{
swap(h[a],h[b]); //交换数值
swap(pk[a],pk[b]); //交换pk映射
swap(kp[pk[a]],kp[pk[b]]); //交换kp映射
}
8.堆的模拟中映射关系:
{
一般堆的定义为:
int h[maxn],kp[maxn],pk[maxn],idx,len;
h[maxn] 表示堆
kp[maxn] 表示堆 第 k 个数——> h中结点(point)编号的映射
pk[maxn] 表示堆 结点编号为 p 的 结点——> h中第 k 个数的映射
idx表示 已经插入 过 多少结点
//idx!=len
len表示堆中的所有结点的数量
}
9.小方法:
在构造堆的时候用这个比较快
for(int i=n/2;i;i--) down(i);
模拟堆 代码
#include<iostream>
using namespace std;
int const maxn =1e5+10;
int h[maxn],kp[maxn],pk[maxn],idx,len;
void h_swap(int a,int b)
{
swap(h[a],h[b]); //交换数值
swap(pk[a],pk[b]); //交换pk映射
swap(kp[pk[a]],kp[pk[b]]); //交换kp映射
}
void down(int u) //需要down的根节点
{
int t=u; //t用来存储u的最小儿子
if(2*u<=len && h[u*2]<h[t]) t=u*2;
if(2*u+1<=len && h[u*2+1]<h[t]) t=u*2+1;
if(t!=u) //如果t不相等意味着u还不满足小堆的条件于最小儿子交换后继续down
{
h_swap(t,u); //当有映射关系时需要用这个自定义函数
down(t); //递归
}
}
void up(int u)
{
int t=u; //up中的t保存的是父结点
if(u/2>0 && h[u/2]>h[t]) t=u/2; //up操作中只需要判断up儿子与根的大小就可
if(t!=u) //递归操作
{
h_swap(t,u);
up(t);
}
}
int main()
{
int n; cin>>n;
while(n--)
{
string aim; cin>>aim;
if(aim=="I")
{
int x; cin>>x;
//insert(int x)操作
h[++len]=x;
pk[len]=++idx;
kp[idx]=len;
up(len);
}
else if(aim=="PM")
{
cout<<h[1]<<endl;
}
else if(aim=="DM")
{
h_swap(1,len--);
down(1);
}
else if(aim=="D")
{
int k; cin>>k;
int u=kp[k]; //一定要注意这个地方
/*
我之前是这么写的
{
h_swap(kp[k],len--)
up(kp[k]);
down(kp[k]);
}
这么写会在h_swap之后 kp[k]其实是len,所以要提前保存在u中
*/
h_swap(kp[k],len--);
up(u);
down(u);
}
else if(aim=="C")
{
int k,x; cin>>k>>x;
h[kp[k]]=x;
up(kp[k]);
down(kp[k]);
}
//for(int i=1;i<=len;i++) cout<<h[i]<<" ";
// cout<<"---------"<<endl;
}
}