c语言sort_堆排序-C语言实现

前言

我们在介绍《什么是优先队列》的时候就注意到,如果每次都删除堆顶元素,那么将会得到一个有序的数据。因此,我们可以利用二叉堆来对数据进行排序。

堆排序分析

通过前面的学习我们可以看到,如果构建一个二叉堆,最后每次从堆顶取出一个元素,那么最终取出元素就是有序的,不过如果要用来对数据按照从小到大排序,就不是构造小顶堆,而是大顶堆了,即堆顶元素大于等于其左右儿子节点。总结堆排序思路如下:

  • 以O(N)时间复杂度构建N个元素的二叉堆
  • 以O(logN)时间复杂度删除一个堆顶元素,N个元素时间复杂度为O(NlogN)
  • 由于删除一个堆顶元素时,就会空出一个位置,为了节省空间,将删除的堆顶元素放到数组末尾
  • 当堆为空时,完成排序
  • 由于数组元素从下标0开始,因此每个位置必须利用好。假设堆顶元素位置为i,那么左右儿子节点位置分别为2i+1,2i+2

实例分析

根据前面的分析,我们来看一个具体的例子。假设我们要对

1 10 8 5 7 15 35

进行排序。

该数据构成的原始二叉树如下:

37f30e1477089d74c588ef41c292c5b9.png

构建二叉堆

为了能够使得数组所有元素满足堆的性质,即父节点大于等于儿子节点,我们需要从倒数第二层开始调整(为什么不是从最后一层?)。即调整8和10。
对于8来说,找到它的儿子节点中较大的一个,即35,将8和35交换后如下:

28a639519333b243eb201fbdee07bc43.png

此时数组数据为:

1 10 35 5 7 15 8 

对于10来说,它比左右儿子节点都大,因此不需要调整。

对于1来说,它的右儿子35最大,因此需要调整,和右儿子交换后如下:

c2e42cd1e79809242c9912401312666b.png

此时数组数据为:

35 10 1 5 7 15 8 

但是一次交换后,我们发现,1的左儿子还是比它大,因此交换它和较大的左儿子的位置,交换后如下:

d3b13f541a7becc6db9205ce3fdc57bd.png

此时数组数据为:

35 10 15 5 7 1 8

最终我们得到了满足堆性质的二叉堆了。

基于二叉堆的排序

在堆创建好后,每次取出堆顶元素,并且调整堆,把堆顶元素放在数组最后即可。
例如,对于前面创建好的堆,堆顶元素是35,我们取出第i(此时为1)个元素35,并把堆最后一个元素放在数组倒数第i个位置:

0f7dff9eaeb597fc7a3574696856a1da.png

为了满足堆性质,我们需要调整堆顶元素,因为堆顶元素目前不满足堆性质,因此需要交换8和15的位置:

ecf115af0d10cffc21f17f7c2c0bf301.png

此时所有元素再次满足堆性质。

此时数组数据为:

15 10 8 5 7 1 35

对于其他元素也是同样的操作,因此不再赘述。

代码实现

根据上面的分析,关键代码实现如下:

void 

完整可运行代码地址:heapSort

运行结果:

before sort:1 10 8 5 7 15 35 
after  sort:1 5 7 8 10 15 35

总结

结合我们前面介绍的优先队列,我们很容易理解堆排序,不过需要注意的就是位置0必须使用上。另外通过利用删除堆顶元素后空出来的位置,避免了另外申请数组内存来存放排序好的数组。建议自己修改完整可运行代码,来观察数据调整情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值