1. 先验知识

堆排序,是使用一种 数据结构的排序算法。在了解堆排序前,建议先掌握二叉树相关知识,不然很费劲,很迷茫。

好了,话不多说。接下来先讲一讲 堆 这种数据结构的特点。主要有两点:①堆是一颗完全二叉树;②堆有大顶堆和小顶堆之分。

1.1 满二叉树

在讲完全二叉树之前,需要先了解一下 满二叉树,满二叉树就是一颗二叉树,每一层的节点都是“满”的,如下图。

1.2 完全二叉树

将树中的节点从上至下,从左至右顺序编号,如果和满二叉树相同编号节点的位置相同,那么就是完全二叉树,如下图。

1.3 大顶堆和小顶堆

大顶堆,很明显就是顶点数据比较大的堆;小顶堆则是顶点数据比较小的堆。话不多说,看图。

2. 堆排序

好了,第一节费了点时间,如果还不够详细的地方,可以评论或者自己网页搜索一下哈,再不进入主题,黄花菜都凉了。不知道大家根据前面讲解的大顶堆和小顶堆得到什么结论没有?如果没有结论,那我这里帮你总结一下,主要有两点:①大顶堆和小顶堆从上至下,从左至右不是完全有序的,如上面大顶堆扫描得到 {34, 18, 23, 12, 8};小顶堆得到 {3, 9, 7, 9, 12};②大顶堆的根节点是最大值,小顶堆的根节点是最小值。

根据大顶堆的根节点是最大值这个特性,我们可以通过得到大顶堆,取出根节点,然后循环调整大顶堆取出根节点即可得到有序数列。同理,小顶堆也可以。一般大顶堆适用于升序排序,小顶堆适用于降序排序,不是很好分析为什么,下面走一遍你就会体会到,如果没有体会,我们后面再分析;

2.1 堆排序原理

 以大顶堆对数组进行升序排序为例。

① 将待排序数组调整为大顶堆;

② 此时根节点是最大值,交换根节点(数组首元素)和末尾节点(数组待排序元素的最后一个元素,示例会详细说明);

③ 因为根节点和末尾节点交换了数据,需要重新对根节点开始递归调整,其它节点没有变化不需要调整;

④ 重复第②和③步骤,直到最后剩下一个待排序元素。

到这是不是有点理解堆排序的原理,什么?还是一脸懵!那就只能开始分析示例了,接下来我们实际走一遍堆排序的流程。

2.2 堆排序示例

假设初始无序数组arr = {3, 9, 13, 7, 1, 16, 3, 11},根据完全二叉树的构建规则,得到数组对应的完全二叉树形式如下。

2.2.1 将数组调整为大顶堆

① 得先将该数组调整为大顶堆形式对应的数组,怎么将完全二叉树调整为大顶堆呢?我们知道大顶堆的节点值要大于或等于子节点值,所以我们得从树的最底层开始,慢慢地把数值大的节点往上放,最终最大值放在根节点。

② 我们从哪个节点开始调整呢?对于每一个节点,它要和子节点比较,判断是否小于子节点,如果小于子节点则交换,否则不调整。所以我们从拥有子节点的节点开始,也就是非叶子节点。

③ 通过完全二叉树的结构图,我们知道是从 第三层的7 开始调整。但如果是写代码,那我们怎么“确定”是 第三层的7 呢?

④ 得益于完全二叉树的结构,下标为index的节点,它的父节点下标是 (index - 1) / 2,左子节点下标是 2 * index + 1,右子节点下标是 2 * index + 2;完全二叉树的最后一个节点,它肯定没有子节点,那么我们可以有个策略:从完全二叉树的最后一个节点往前遍历,每次遍历一个节点,我们就寻找它的父节点,然后父节点和左右子节点进行比较、调整,直到根节点。这样最终我们就能得到一个大顶堆。

⑤ 第一次,我们从完全二叉树最后一个节点开始,它在数组的下标是 arr.length - 1 = 7,它的父节点在数组arr中的下标可推算得 (arr.length - 1 - 1) / 2 = 3,刚好是完全二叉树 第三层的7 的编号。

⑤ 完全二叉树 第三层的7 小于左子节点 第四层的11,也就是数组 arr[3] < arr[2 * 3 + 1];所以交换,得到 arr = {3, 9, 13, 11, 1, 16, 3, 7},对应的完全二叉树如下

⑥ 第二次,我们从完全二叉树倒数第二个节点分析,也就是数组索引往前,指向 arr.length - 2 = 6,它的父节点在数组下标可推算得 (arr.length - 2 -1) / 2 = 2,指向完全二叉树 第二层的13。

⑦ 由于完全二叉树 第二层的13 小于左子节点 第三层的16,也就是 arr[2] < arr[2 * 2 + 1] ,交换得 arr = {3, 9, 16, 11, 1, 13, 3, 7},对应的完全二叉树如下

⑧ 接下来同样处理即可,就不再具体描述,以下只做简单分析。

⑨ 第三次,完全二叉树倒数第三个节点 第三层的13 ,也就是数组索引为 arr.length - 3 = 5,该节点的父节点 第二层16 ,数组的下标是 (arr.length - 3 - 1) / 2 = 2。满足条件不需要调整。

⑩ 第四次,完全二叉树倒数第四个节点 第三层的1 ,也就是数组索引为 arr.length - 4 = 4,该节点的父节点是 第二层9 ,数组的下标是 (arr.length - 4 - 1) / 2 = 1

11. 由于 第二层的9 小于左子节点 第三层的11, 也就是 arr[1] < arr[1 * 2 + 1],进行交换;交换完之后,得到 arr = {3, 11, 16, 9, 1, 13, 3, 7},对应的完全二叉树如下。

12. 此时还应递归判断完全二叉树刚交换完得到的 第三层的9 是不是符合大顶堆要求,我们这个示例刚好符合,则结束递归,否则要递归判断并调整。

13. 第五次,完全二叉树倒数第五个节点 第三层的9 ,也就是数组索引为 arr.length - 5 = 3,该节点的父节点是 第二层11 ,数组的下标是 (arr.length - 5 - 1) / 2 = 1。满足条件不需要调整。

14. 第六次,完全二叉树倒数第六个节点 第二层的16 ,也就是数组索引为 arr.length - 6 = 2,该节点的父节点是 根节点3 ,数组的下标是 (arr.length - 6 - 1) / 2 = 0

15. 由于 根节点3 小于右子节点 第二层的16, 也就是 arr[0] < arr[0 * 2 + 2],进行交换;交换完之后,得到 arr = {16, 11, 3, 9, 1, 13, 3, 7},对应的完全二叉树如下。

16. 此时还应递归判断完全二叉树刚交换完得到的 第二层的3 是不是符合大顶堆要求,我们发现节点 第二层的3 小于左子节点 第三层13,也就是 arr[0 * 2 + 2] < arr[(0 * 2 + 2) * 2 + 1],进行交换。

17. 交换后得到 arr = {16, 11, 13, 9, 1, 3, 3, 7},对应的完全二叉树如下。

18. 递归判断刚交换完的 第三层3 是不是符合大顶堆要求,我们发现它没有子节点,并且推算的子节点下标超出了数组的末尾索引,结束递归。

19. 因为我们刚刚调整的就是根节点,所以结束扫描。最终大顶堆结构图如上图所示,数组 arr = {16, 11, 13, 9, 1, 3, 3, 7}

至此,我们完成了一次数组调整为大顶堆的流程。

但是上面的分析是从最后一个节点慢慢往前扫描、调整,效率有点低。但事实上由于完全二叉树的特点,我们可以直接从最后一个节点(一定是叶子节点)的父节点(这个节点是完全二叉树的最后一个非叶子节点,它后面的节点都是叶子节点,不用调整)开始往前扫描、调整。可以仔细思考、理解一下,我就不详细解释了。

2.2.2 将大顶堆的根节点与末尾节点进行交换

在进行了2.2.1之后,我们得到了大顶堆,此时我们需要保存大顶堆根节点(最大值),保存之后,我们还需要对剩下的数据进行调整得到大顶堆,再保存大顶堆根节点,周而复始,直到最后只有一个元素,此时不需要再排序,结束。问题来了,每次保存大顶堆根节点很简单,我们可以新建一个数组,然后依次将数据保存在该数组即可。我们同时需要将该根节点从数组中“刨除”,然后对剩余数据继续调整为大顶堆。如我们上面得到的数组为{16, 11, 13, 9, 1, 3, 3, 7},如果把数组首元素“刨除”,也就是“刨除”大顶堆根节点。那么我们接下来需要对剩余元素{11, 13, 9, 1, 3, 3, 7}进行排序,我们需要重新构建完全二叉树,并且调整为大顶堆,然后保存大顶堆根节点,然后再将该元素“刨除”,周而复始对剩余数据构建、调整。

但是这样有点复杂,效率不够高,因为我们第一次得到的大顶堆是大致有序的,如果后续每次都要“刨除”根节点,重新构建、调整的话,效率大打折扣。所以这里有一个操作:直接将数组首元素和数组末尾元素进行交换。这样交换之后,我们只对数组下标为 0——arr.length-2 的元素进行调整,并且只需要对大顶堆的根节点递归调整即可,因为其它的节点经过第一次调整已经满足大顶堆的需求,这样既简单又高效。

2.3 堆排序的Java代码

分析了这么多,终于来到了码代码阶段,以下是Java的堆排序代码、相关代码解释以及排序效果,如下图。

手把手详解堆排序,堆就这么难懂?没有人看一遍学不会的,如果学不会,那就两遍吧相关推荐

  1. js排序算法详解-堆排序

    全栈工程师开发手册 (作者:栾鹏) js系列教程5-数据结构和算法全解 js排序算法详解-堆排序 这种排序方式呢,理论性太强,看动图的时候满脸写着懵逼,多看几遍似乎明白了编者的意图,但是要把这种理论的 ...

  2. 算法-详解堆排序算法

    title: 算法-详解堆排序算法 date: 2017-07-06 22:00:16 categories: 算法,面试 tags: [算法,Algorithm,面试,排序] description ...

  3. android 双层饼图_高大上的Excel双层饼图 充分表达层级关系 手把手详解

    效果图预览: 高大上的Excel双层饼图 充分表达层级关系 手把手详解 操作步骤: 步骤一:准备好原始数据. 高大上的Excel双层饼图 充分表达层级关系 手把手详解 步骤二:分类汇总数据 1.选中原 ...

  4. 【最新】windows电脑FFmpeg安装教程手把手详解

    [最新]FFmpeg安装教程手把手详解 写在前面 一.下载&解压 二.配置环境变量 1.配置系统环境变量 额外补充 写在前面 本文以 Windows 64 位操作系统为例演示 一.下载& ...

  5. python java混合编程_详解java调用python的几种用法(看这篇就够了)

    java调用python的几种用法如下: 在java类中直接执行python语句 在java类中直接调用本地python脚本 使用Runtime.getRuntime()执行python脚本文件(推荐 ...

  6. 主管护师计算机考试如何舞弊,人机对话操作步骤详解,2020主管护师考生必看!...

    原标题:人机对话操作步骤详解,2020主管护师考生必看! 距离2020年卫生资格考试不到100天了. 2020年度卫生资格考试除初级护师外采用人机对话的方式进行.很多考生第一次接触人机对话考试形式,那 ...

  7. 思科SP CCNP组播侦听者协议MLD详解想要成为高级网络工程师必看-ielab

    思科SP CCNP组播侦听者协议MLD详解想要成为高级网络工程师必看-ielab,网桥(Bridge)是早期的两端口二层网络设备,用来连接不同网段.网桥的两个端口分别有一条独立的交换信道,不是共享一条 ...

  8. 详解:什么是眼图、眼图怎么看?

    详解:什么是眼图.眼图怎么看? 2019-10-16 15:15:44 燚智能物联网 简介 眼图是指利用实验的方法估计和改善(通过调整)传输系统性能时在示波器上观察到的一种图形. 观察眼图的方法是:用 ...

  9. 【算法知识】详解堆排序算法

    点击蓝色字关注我们! 什么是堆 「堆」首先是一个完全二叉树,「堆」分为「大顶堆」和「小顶堆」: 「大顶堆」 : 每个节点的值大于或等于其左右孩子节点的值,称为大顶堆. 「小顶堆」同理就是每个节点的值小 ...

最新文章

  1. 苹果智能车芯片已基本就绪!最新造车进展曝光,股价一夜暴涨4000亿
  2. JAVA WEB知识总结之一--入门
  3. Python强大的格式化format
  4. 英国文化影响管理风格_文化如何影响用户体验
  5. 【CodeForces - 798D】Mike and distribution (思维构造,贪心,黑科技)
  6. Evernote是什么软件?印象笔记for mac V10.3.6官方版
  7. 电影院订票选座小程序 开题报告
  8. 第一节计算机课开场白,老师第一节课的开场白
  9. 从零开始制作一款打卡类小程序
  10. 华三模拟器配置IS-IS
  11. PDF合并在电脑上怎样实现?PDF合并的方法有哪些?
  12. idea类存在找不到解决办法
  13. html怎么混合颜色,在CSS中为背景混合两种颜色
  14. 041孙悟空第三人称视角
  15. 16 张图带你搞懂 Java 数据结构,从此想不飘都难!
  16. Linux系统下,绑定USB串口设备号,解决上电USBID随机情况
  17. 大学计算机三级网络技术,考前数天如何突破性通过计算机等级考试之三级网络技术篇...
  18. 计算机话筒技术指标,话筒指标与调音台电平
  19. Thomas Kurian诠释甲骨文的云创新
  20. Unity中使用柏林噪声生成地图

热门文章

  1. SAP配置webdynpro完全手册 .
  2. 感恩八年 — 致CSDN (感谢有你)
  3. 查看凭证更改记录的三种方式
  4. 如何修改SAP标准数据元素文本
  5. 现金流为王!中小企业如何“疫”境求生?
  6. 凡普金科以互金 “头马”入选互联网企业百强的启示:创新为王
  7. Linux程序内存跟踪,分享一款Linux进程和内存活动监视神器
  8. sql 判断记录是否存在_判断数据库是否存在该条记录,count(0) or limit
  9. mate 10android o主题,Mate10不仅硬件强,还有安卓8.0
  10. linux内核如何修改lowmem,技术内幕:Android对Linux内核的增强 Low Memory Killer