题目描述
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;
}
}
请问,删除第k个插入的数这个操作,由于是小顶堆,因此交换ph[k]和cnt之后,只会换过来一个大值,可是我只down就错了,大佬求解
/*
1
5 3
6 6 4 4
如果我删除 下表为4 数值是6 这个点
就会发生 4<5 所以要up
*/
哇,感谢!之前理解错了,应该是小顶堆只确定每棵子树的根节点最小,至于左右子树谁大谁小并不确定。而只有位于同一棵子树才有层数越往下节点值越大,这里离交换过来的有可能是另一棵子树的节点值,自然可能有大有小
hh 没事 算法就是玄学 咱们以后多多交流hh
好的好的
感谢
强
%%%%%%%
努力终究会有回报加油!
up操作那里为啥要只能小于而不能小于等于呀,求解
else if(s==”D”){
cin>>x;
为什么我这样写就会有问题呢?
这样就把ph[k]这个值变成si了
所有交换都要用h_swap。。。我就卡这了
大佬我想请问一下,为什么up()函数里,父节点只与一个子节点比较,没有像down()函数那样与两个子节点比较呢
因为原来本就符合稳定堆,此时如果需要up 那最小的肯定在根
应该理解为相应节点只需要和其父节点比较,比父节点小就上移
感谢大佬!!!D那里一直是按照错误写法写的~~找了半天才看到这篇题解~
醍醐灌顶,感谢大佬
大型 扮猪吃老鼠 现场,我选择当场去世
大佬教我敲代码
大佬tql,orz
哥你别骂了别骂了 大佬教我写代码。。。。
666
开头的递归down操作这一行代码有误, 应为
2 * u + 1 <= len
谢谢 马上该 敲的时候手误了 谢谢谢谢
大佬有不对的告诉我,小弟谢谢了
不是大佬hh
《该》