C语言实现堆(Init Push Pop Empty Size Top AdjustUp AdjustDown Destroy)

文章详细介绍了堆这一数据结构,特别是作为排序工具时的作用,如STL中的priority_queue。堆的底层实现基于顺序表,逻辑结构为完全二叉树。文中讲解了如何通过初始化、插入(Push)、删除(Pop)操作来维护大堆的性质,以及向上调整和向下调整算法的原理,同时也提及了小堆的变体。
摘要由CSDN通过智能技术生成

堆是数据结构里面一个重要的存储结构,而且堆也可以实现排序作用,其中的stl里面的priority_queue(优先级队列)的底层用的就是堆排序,所以而且建堆排序的效率也很高,所以堆很重要,今天我们就讲一下堆的实现,这里说一下堆的底层其实就是顺序表。

下面我呢就来看一下堆的数据结构是什么样子

堆的结构和顺序表的一样,因为堆的底层空间就是顺序的,但是堆的逻辑结构其实是一棵完全二叉树

502892d1bc244fa1993a6c38ce98e9b2.png

这里就不讲他的结构了,下面我们会讲到他的逻辑结构

我们这里先看一下二叉树(完全二叉树)是什么样子 

c019313d7fc346a5a223e4fead1572dd.png

 好了就是这个样子,这里就假设你已经知道了二叉树或者完全二叉树了

如果不知道的话可以先去了解一下

下面我们看一下他们的关系

其中每一棵树都分为根节点和孩子节点

a7525ca270ae4bb0a3b2ba044fa5eeb5.png

这里是这个样子的,他的根节点就是顺序表的第一个位置,所以他就是下标为0的位置,所以让根节点为0,这样我们就可以知道每一个孩子节点和父亲节点的关系了

下面我们来看

其中假如节点3,那么他的 父亲节点就是(3-1)/2,就是这样,而他的右孩子节点也是这样计算,不相信的话可以算一下,那么如果要算2节点的孩子节点呢?

下面在看一下,如果2节点的孩子节点其中有两个,而左孩子节点就是(2*2)+1,我们可以看到确实为5这个节点,而他的右孩子节点也就是理所当然的在多加一个1就可以了

OK这里树节点已经说清楚了,这里说的后面会有用处

下面开始大概说一下里面会实现的函数有哪些

53f38a02c6544beab998f202a2c3a58c.png

OK,就是这么一些,这个和栈比较多了一个Adjustup(向上调整算法) 和AdjustUp(向下调整算法),OK我们下面仔细看一下

首先还是老样子

初始化函数

57129d65d2f040cf82e744f89f365462.png

这里和之前都是一模一样的(不懂的可以看我之前的顺序表和栈等...)

OK下面还是Push这个有意思,下面细说

58219e1f9e2447f6bfcbe6bc4c9cfdaa.png 

这里也能看到,刚开始是断言检查,然后就是检查扩容,后面就是先把数据插入进去,那我先把扩容给大家看一下,之前也说过,这里主要讲向上调整算法

014ddd1f57494ede87eda31032b405a0.png 

这里大家看一下吧,写这个函数主要是为了让其他函数看起来不那么臃肿,没有别的意思

那么我们就看一下向上调整算法,和为什么要像上调整

首先我们要知道,我们这里写的是一个大堆,大堆的特点就是每一个根节点的值都大于两个孩子节点,所以我们来看向上调整算法

bb425d4ce02b4ad3842be1dd83dbe22d.png 

我们也来看一下为什么要像上调整

首先假设我们要插入这些数据

cb755f0660124e1f83f2457f7fd070b5.png 

就是后面这些,那么我们当然就是第一个要插入2,然后就要插入34就像下面这样

8817a253c1c6460eafcf48f9eeadcfa0.png

但是由于我们这个是大堆,每一个根节点都大于两个孩子节点,所以这里明显是孩子节点大于根节点的,所以这里需要把孩子节点向上调整,这里就要向上调整

我们来看一下

这里假设一个已经插入一些数据的值了,因为只有两个节点或者是一个一个插入太难讲了,我们先假设前面的已经插入好了而且也调整好了,不过这里新插入的节点和一个一个插入都是差不多的,所以我们就假设我们已经把刚才的数据插入进去了41e19c462f764e7b8207e892b450cee9.png 

我们假设已经把刚才的数据插入进去了,但是这里我们继续插入一个45这个值

663ad261ca9540de9caa7412530e69e8.png 

那么我们刚开始肯定会插入到这里,但是这个节点不满足大堆,所以需要对这个节点进行向上调整

我们就来看一下如何调整

这里我们看到他比他的父亲节点的值要大,所以我们需要让他和他的父亲交换 

c7cc737eca3f4a71aaaf5d2dc473e26b.png

所以需要交换他们两个

b785a491acda41ee9bc0aa2dc35ab433.png 

交换之后就是这样,但是他还是不满足,因为45还是大于他的父亲节点34所以还需要交换他们两个

72912f90a8cb4778bbcceadc16f01cab.png 

所以继续交换

7883677f4ca145cdaaf41bd2910b135c.png 

交换后就成了这样,我们现在在看,他已经满足大堆了,所以我们也不需要继续调整了

这时候我们就插入完毕,所以这时候我们在看看向上调整代码就很容易了

fe5d9833607a499286f2b9f16c8953c9.png 

这里我们把孩子节点的位置传过来了,所以这时候我们就需要计算出父亲节点的位置,前面已经说过了父亲节点的位置如何计算,而且上面代码也有写,这里就不说了,然后这时候我们就需要一直判断因为这里有可能调整一次后还是不满足,就像上面插入45后调整了两次,所以这里我们是需要用循环的,这里结束条件就是直到孩子节点调整到根节点,或者就是节点已经满足大堆的条件就不用交换了,所以这里我们如果大于的话我们就用Swap函数交换,如果已经满足了大堆的条件我们就直接break退出就行了,如果已经交换一次我们就需要判断下一个满不满足,所以我们把父亲节点的位置给孩子节点,然后再计算父亲节点的位置就是这样

这里先给你们看交换函数其实很简单

99ef893a89ce439498f6eed015245c1e.png

 

下面我们看一下Pop

 9fb87b732ec5416dbc523d4ba6bcfe2f.png

这里就是Pop函数这里删除我就直接说思路了,由于这个低层和顺序表类似,而且对于堆来说删除哪一个数据有意义?当然,这里我们只要稍微想一下我们这个堆是大堆所以第一个数据的值是最大的,所以我们删除的话也是删除第一个数据,而如果我们直接删除第一个数据的话那肯定是不行的,不仅会打乱后面节点之间调整好的顺序,而且头删的话效率也特别低

所以这里我们可以让第一个数据和最后一个数据交换,然后删除最后一个数据,然后就是对第一个数据进行向下调整算法,我们先看一下这向下调整算法的思路是什么样子的

现在先假设我们还是最上面的数据,假设我们没有插入45的数据

d4de8f96ebc94927b04565ff5a25b61f.png

我们的数据先是这个样子的,然后就是我们第一个数据和最后一个数据位置交换

 e2a445d7c3d341d09b1b842df3a2fdd2.png

 

 所以我们就交换

97b39be938784e8b9f46ff545ec5a61d.png

这就是交换以后,然后我们还要删除最后一个元素

52a22967713344f6b34af9e72220f42f.png 

然后我们删除之后,由于我们这个是大堆,每一棵树的根节点都大于两个子节点,所以我们第一个数据肯定是最大的,所以我们 这时候就要和第一个位置的两个孩子位置的数据进行比较,找两个孩子里面更大的那个,如果父亲位置的数据大于最大孩子位置的数据,那么就不用交换了,如果小于的话,就和较大孩子位置的数据交换,然后继续比较,思路就是这样这时候我们在看代码

18ad424e3edd426ca6d6c203938db4f5.png

我们这里先计算一下孩子位置,之前说过如何计算孩子的位置,如果我们孩子的位置大于数据总个数,那么就退出,然后这时候我们还是用循环,由于我们刚开始只看了一个左孩子位置的数据,因为这里还有可能有有孩子,所以这里也要看一下右孩子位置的数据和左孩子位置的数据的大小,让child变量记录两个孩子里面数据较大孩子的位置,由于这里的右孩子不一定存在,所以这里还需要判断右孩子位置是否合法,如果这时候都合法,并且孩子位置的数据大于父亲位置的数据,那么就交换,如果不大于的话就直接break,如果已经交换完了的话,那么这时候我们就更新父亲位置,把孩子位置给父亲,然后再计算孩子的位置,进去新一轮循环,知道孩子位置大于数据总个数或者,父亲位置数据已经满足大堆的条件,就是这样

这时候我们主要的内容已经讲完了,不过上面在Pop的时候用了Empty函数,这个函数也很简单

28cc7ef823b147828a738459a165bae2.png 

很简单,如果size等于0就说明为空

下面看一下size函数

78c8b3e3d61048399b04b3960ed28702.png 

也就是返回size就可以了

下面在看一下,取第一个位置的元素也就是取堆顶数据

ed3e04d5a9ea45b68d1498dc034fdc28.png 

就是返回a位置的第一个元素

最后就是销毁了

c55c71af14854c6cae95c1458621fc9b.png 

很简单,直接free就可以了

最后补充一句,其实还有小堆,小堆的话只需要改变向上调整和向下调整里面父亲位置和孩子位置比较的大于小于符号就可以了

例如向上调整,如果孩子小于父亲就交换

向下调整,如果父亲大于孩子就交换

就是这样,下去可以自己试一下

OK game over 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naxx Crazy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值