【玩转二叉树③】C语言堆排序详解(动图演绎)

一、前言

①前言
 在上一篇博客里,我们所写的堆排序的问题在于,需要额外占用O(n)的空间,并且需要预先写很多堆的函数接口,显示这不是一个理想的堆排序。在这篇博客里,我将向大家展现堆排序真正的C语言实现方式。

②阅读须知
 本篇博客基于【玩转二叉树②】,相应接口直接使用而不再本文赘述。所以请大家一定先参阅前一篇博客

二、堆的创建

1.向上调整算法

①O(1)的空间复杂度
 既然要将空间复杂度降为O(1),那我们就不能额外申请堆空间,而是需要在原数组上建堆。下面向大家展现两种建堆(向上调整,向下调整)的方式:

②向上调整算法介绍
 其基本思想就是「❉」HeapShiftup()函数。我们要做的就是遍历数组,将数组中的每一个元素“压入堆”中,这个压入的过程实际上是对堆的【逻辑结构】的维护。由于数组的空间一定是足够的,所以不需要考虑扩容问题。

③向上调整动图图解
在这里插入图片描述
【过程分析】

  1. 以数组[1, 2, 3]为例进行分析
  2. 首先只有1个元素1,位于堆顶
  3. 压入元素2,元素2通过上浮操作到底应该到的位置(元素3同理)


④源码解析
在这里插入图片描述
【重难点分析】

  1. 我们从数组的第二个元素开始上浮插入,所以i从1开始
  2. 因为堆属于一种完全二叉树,所以不用担心后面的数据被覆盖,只有当前数据和前面的元素之间才会放生交换

⑤时间复杂度分析:
计算时间复杂度时我们考虑最坏的情况:「❉」即上浮到根节点.
在这里插入图片描述
对每一层进行相加求和,利用高中所学的「❉」错位相减法 ,可以求出和为
在这里插入图片描述

2.向下调整算法

①向下调整算法介绍
 向下调整算法的基本思想「❉」HeapShiftdown()函数。向下调整算法有一个基本前提:左右子树必须是一个堆。
 以下图为例进行说明,由于结点27的左右子树都是堆,所以左孩子和右孩子分别是左树和右树的最小值:

  1. 27与15, 19比较,15是最小值 ∴27与15交换
  2. 27与18, 28比较,18是最小值 ∴27与18交换
  3. 27与40, 25比较,25是最小值 ∴27与25交换在这里插入图片描述

完成上述交换后,所有的结点都满足了堆的逻辑结构。构造成功

②源码解析
在这里插入图片描述
【重难点剖析】

  1. 由于叶节点没有子节点,所以单个元素自身可以看成是大根堆,也可以看成是小根堆。因此我们从叶节点的上一层开始遍历。
    在这里插入图片描述

  2. 最后一个叶节点的父节点:(arrSize - 1) - 1 >> 1;【叶节点与父节点的下标关系】

  3. 我们从最后一个结点的叶节点开始进行「❉」HeapShiftdown() 操作

  4. 下标减减到达前一个结点(见上图金茶色箭头),由此对每一个结点都进行下沉操作,最终构造出堆

③时间复杂度分析
同样考虑最坏的情况,照下图进行推导在这里插入图片描述
【总结】
 我们可以看到向下构造的时间复杂度为O(N),低于向上构造的时间复杂符O(N*logN)。所以我们选用向下构造法构造我们的堆。

三、堆排序的实现

①如何利用堆进行排序?
【问】如果我们想实现升序,可以创建小根堆吗?
【答】不可以。虽然堆顶元素就是最小值,因为我们是在原数组操作,所以不能用HeapPop函数,所以无法再继续排序。

②结论

  • 实现升序需要创建大根堆
  • 实现降序需要小根堆


③源码剖析
在这里插入图片描述
【重难点剖析】

  1. 我们不能从堆顶进行操作,只能弹出堆尾的元素,这样才不会影响之前的逻辑结构
  2. 对于大根堆,堆顶元素就是最大值,与堆尾元素进行交换,再将sz减减
  3. 对堆顶元素HeapShiftDown,维护堆的逻辑结构
  4. 重复上述过程,实现对数组的升序排列


④堆排序的本质
堆排序的本质上其实是选择排序,先选取最值放在末尾,再选取次最值放在倒数第二的位置,以此类推。
 对于传统选择排序来说,选出当前元素中最值的时间复杂度为O(N),所以选择排序总的时间复杂度为O(N^2)
 对于堆排序来说,选出当前元素中的最值所需要的时间为O(logn),时间花在ShiftupDown函数对堆结构的维护。所以堆排序总的时间复杂度为O(N*logn)

四、TOP-K问题

①问题描述
求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大


②解决办法①:排序
 问题在于数据量很大比如几百亿的时候,内存存储不下这么大的数据量,所以排序的方法不可行

③解决办法②:构建大小为K的堆
 我们每次只需从磁盘中读取一个元素,并继续维护我们的堆,这样最终就可以得出TOP-K。利用堆这种数据结构可以极大的节省内存空间。

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罅隙`

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

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

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

打赏作者

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

抵扣说明:

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

余额充值