堆の学习笔记

前言

关于这篇文章其实已经咕了 6.5 个月。。。

现在终于打算动了。

正文

首先堆是什么?

堆是一种数据结构,可以高效地以 O(logn) 的复杂度维护一个集合的最值。(支持插入任意值、删除最值和查询最值操作)

这种数据结构在一定程度上借助完全二叉树实现,整体的实现思路是将堆抽象成一棵完全二叉树,然后根据完全二叉树的特性巧妙地维护最值。

这里附张图(搬运):

如果将完全二叉树的节点按上到下的次序标数,i 点的左儿子为 2i,右儿子为 2i+1于是我们便可以大搞特搞于是可以用一个数组 d 记录完全二叉树的状况,di 表示 i 点当前的数。

然后为了方便维护最值(这里维护最小值)我们可以给堆加性质:

  1. 堆的任意父亲点 左儿子和右儿子。

  2. 堆必须时刻是完全二叉树。

这时最上面的根点一定是整个堆中最小的,我们只要不断维护堆的形态使性质成立,那么就很容易取到最小值。

Del 操作(删除最值):

最值一定是根点,所以我们只要删掉根点就行了。

假设这是原先的堆:

现在我们删除根点,很显然直接拿掉会让堆乱套,为了维护堆,我们只能把 2 和末尾的点交换一下,之后删除就没有影响了。虽然 8 在根点不符合性质,但是我们可以将 8 向下调整至合适的位置,这样就维护了删除操作。

之后把 8 与左右儿子中小的一个交换(这样交换后才能保证符合性质),一直到已经满足性质,那就可以停止交换了。

给出核心代码(ok 是最后一个点的位置):

void del(){
	swap(d[1],d[ok]);//交换
	ok--;//删除
	int dq=1;
	while((dq<<1)<=ok){//如果当前点还有儿子
		int nex=dq<<1;
		if(nex+1<=ok&&d[nex+1]<d[nex]) nex++;//此时nex为值小的儿子的编号
		if(d[nex]<d[dq]) swap(d[nex],d[dq]);
		else break;//如果不用交换了直接退出
		dq=nex;
	}
}

ins操作(插入任意值):

和上面类似,但这次是在最下面建新的点,然后往上调整。图可以自行脑补就不放了。
核心代码:

void ins(int x){
	d[++ok]=x;//新建
	int dq=ok;
	while(dq){
		int fa=dq>>1;//父亲点i(因为儿子是父亲的2i或者2i+1,整除后一定会得到i)
		if(d[fa]>d[dq]) swap(d[fa],d[dq]);
		else break;//如果不用调整了退出
		dq=fa;
	}
}

查询最值:

只要输出根点 d1 即可,一定是最值。

模板题:P3378

附习题:
P1090

posted @   cj180202  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示