堆排序是一种高级排序算法,时间复杂度为O(nlogn),空间复杂度仅为O(1),对于海量数据使用堆排序是相当高效的。

相比其他排序算法,堆排序确实比较难,不仅体现在思想上,更体现在代码逻辑上。在《数据结构与算法分析(Java语言描述)》所给的堆排序伪代码中,有一个很巧妙的地方,值得我们反复推敲。当然在看代码前,大家先自行搞懂堆排序思想。

堆排序第一个步骤是构造二叉堆(按二叉树理解也没问题)。二叉堆是由数组实现的,假设我们要对一个数组进行升序排列,就得构造大根堆;否则,就得构造小根堆。我们当然是进行升序排列,所以要构造大根堆。

//arr : 数组
//n : 数组前n个数的长度。为什么不是数组长度?因为后面的数是已经排好序的,
//    所以不必再进入二叉堆。这里不理解不要紧,后面堆排序中大家就能明白了。
//i : 从左往右,从下往上第一个非叶结点的数组下标。即 i = n / 2 - 1//另外一个重要的点,下面这个函数只是让以某个非叶结点为根的子堆局部构成大根堆,
//而整个数组构成大根堆是循环遍历的结果。
private static void adjustHeap(int[] arr, int n, int i){int child; //子节点int temp;  //存储结点i的值//下面这个for循环,temp初始化为arr[i]后便永恒不变了(巧妙1);//每轮循环的判断条件是看当前结点是否有左子节点;//子节点成为当前结点(巧妙2),继续向下构造大根堆(因此构造大根堆是一个自顶向下的过程)for (temp = arr[i]; i * 2 + 1 < n; i = child){child = i * 2 + 1;if (child + 1 < n && arr[child] < arr[child + 1])++child; //存在右子节点且节点值比左子节点大(自然会交换最大的)if (temp < arr[child]) //当前节点值小于子节点值,要交换arr[i] = arr[child];//(巧妙3,看下面解释)else break;}arr[i] = temp; //temp最终位置,是最后一次赋值来的,而不是一次次交换的结果
}

上面说了,当前节点值小于子节点值时,逻辑上是肯定要交换的,不然就不是大根堆。但是大家也会疑惑,后面代码压根就不是交换,只是把子节点值赋给了当前结点。

我前面说了,堆排序适合于排序海量数据,在最差情况下,如果每两个数之间都要进行一次O(3)的交换,肯定会带来很大的开销。看到这里,大家又疑惑了,这不是跟我前面说的自相矛盾吗?

大家结合一下代码中的巧妙1,巧妙2,巧妙3。有没有这样一种体会:代码里没有实现交换,但是逻辑上的确交换了。每次构造中,temp是不变的,若有继续向下构造大根堆的机会,依然是用temp来比较,却是早就交换了哈哈!

构造二叉堆是堆排序的核心与重难点,大家一定要反复理解透彻。相比之下,接下来的堆排序不过是循环使用adjustHeap罢了。

public static void heapsort(int[] arr){for (int i = arr.length / 2 - 1; i >= 0; i--) //整体形成大根堆adjustHeap(arr, arr.length, i);for (int i = arr.length - 1; i >= 0; i--){//每次交换大根堆的第一个数和最后一个数int temp = arr[0];arr[0] = arr[i];arr[i] = temp;//交换后的最后一个数已经放在排序后的最终位置//对剩余数重新构造大根堆//(只需对根节点构造大根堆,因为根节点以下的子堆早已是局部大根堆了)adjustHeap(arr, i, 0);}
}

最后我测试以下的堆排序的性能:

public static void main(String[] args) {int[] arr = new int[9999999];for (int i = 0; i < arr.length; i++) {arr[i] = (int)(Math.random()*arr.length);}long start = System.currentTimeMillis();heapsort(arr);long end = System.currentTimeMillis();System.out.println("堆排序耗时:" + (end - start) + "ms");
}

我前面比较过希尔排序的耗时,结合这次的实践证明,堆排序比希尔增量和Hibbard增量下的希尔排序快了1/3,略慢于Sedgewick增量下的希尔排序
《希尔排序在三种著名增量下的时间性能比较》

堆排序代码详解(Java实现)相关推荐

  1. java斐波那契查找_详解Java Fibonacci Search斐波那契搜索算法代码实现

    一, 斐波那契搜索算法简述 斐波那契搜索(Fibonacci search) ,又称斐波那契查找,是区间中单峰函数的搜索技术. 斐波那契搜索采用分而治之的方法,其中我们按照斐波那契数列对元素进行不均等 ...

  2. java编程数据溢出问题_Java数据溢出代码详解

    Java数据溢出代码详解 发布时间:2020-10-05 15:08:31 来源:脚本之家 阅读:103 作者:Pony小马 java是一门相对安全的语言,那么数据溢出时它是如何处理的呢? 看一段代码 ...

  3. java 文件下载详解_Java 从网上下载文件的几种方式实例代码详解

    废话不多说了,直接给大家贴代码了,具体代码如下所示: package com.github.pandafang.tool; import java.io.BufferedOutputStream; i ...

  4. java 委托机制_通过反射实现Java下的委托机制代码详解

    简述 一直对Java没有现成的委托机制耿耿于怀,所幸最近有点时间,用反射写了一个简单的委托模块,以供参考. 模块API public Class Delegater()//空参构造,该类管理委托实例并 ...

  5. java委托机制教程_通过反射实现Java下的委托机制代码详解

    简述 一直对java没有现成的委托机制耿耿于怀,所幸最近有点时间,用反射写了一个简单的委托模块,以供参考. 模块api public class delegater()//空参构造,该类管理委托实例并 ...

  6. java语言链栈_Java语言实现数据结构栈代码详解

    近来复习数据结构,自己动手实现了栈.栈是一种限制插入和删除只能在一个位置上的表.最基本的操作是进栈和出栈,因此,又被叫作"先进后出"表. 首先了解下栈的概念: 栈是限定仅在表头进行 ...

  7. java一个方法排他调用_Java编程实现排他锁代码详解

    一 .前言 某年某月某天,同事说需要一个文件排他锁功能,需求如下: (1)写操作是排他属性 (2)适用于同一进程的多线程/也适用于多进程的排他操作 (3)容错性:获得锁的进程若Crash,不影响到后续 ...

  8. java中math的方法_Java中Math类常用方法代码详解

    近期用到四舍五入想到以前整理了一点,就顺便重新整理好经常见到的一些四舍五入,后续遇到常用也会直接在这篇文章更新... public class Demo{ public static void mai ...

  9. java搜索代码_Java实现搜索功能代码详解

    首先,我们要清楚搜索框中根据关键字进行条件搜索发送的是get请求,并且是向当前页面发送get请求 //示例代码 请求路径为当前页面路径 "/product" 当我们要实现多条件搜索 ...

  10. java 搜索_Java实现搜索功能代码详解

    首先,我们要清楚搜索框中根据关键字进行条件搜索发送的是Get请求,并且是向当前页面发送Get请求 //示例代码 请求路径为当前页面路径 "/product" 当我们要实现多条件搜索 ...

最新文章

  1. Select()系统调用及文件描述符集fd_set的应用
  2. 通信专业学python有用吗-通信人不会这项技能,5G时代很难拿到高薪
  3. 使用xsodata文件将SAP HANA CDS view暴露成OData服务
  4. Tokenisation word segmentation sentence segmentation
  5. 电子计算机的速度快精确度,都21世纪了,还不来了解了解什么叫compute?
  6. BPF 之巅:洞悉 Linux 系统和应用性能
  7. 艾司博讯:拼多多拼单人数不够怎么取消
  8. Medusa 破解centos密码
  9. 通过Fildder下载百度音乐里的收费歌曲
  10. 快速识别图中文字的操作方法
  11. elasticsearch中基于slop参数实现近似匹配
  12. MCMC如何满足细致平稳条件?
  13. 强力推荐90个优秀外国英文网站
  14. 电脑调分辨率黑屏了怎么办_电脑调分辨率黑屏了怎么办
  15. thinkpad linux win7,Thinkpad t440 Win7+ubuntu双系统
  16. 真假美猴王-Numpy数据与Python数组的区别与联系
  17. 谷歌卫星地图下载器有哪些那款好用
  18. 比尔盖茨 1974 年简历曝光
  19. 如何使用origin跨工作簿进行公式计算
  20. 基于链表的多级菜单设计

热门文章

  1. 【台词】严厉的愛Tough Love」(后妈茶话会)
  2. Python科学计算初探——余弦相似度
  3. c#如何实现叫号操作_C#银行排队叫号系统
  4. WangEdit富文本编辑器图标修改
  5. 三个杯子的倒水问题(BFS)
  6. 深入浅出Python的抽象基类
  7. OpenCV教程:超详细的OpenCV入门教程,值得收藏
  8. 聚焦MSI:三星玄龙骑士电竞显示器陪你畅玩一夏
  9. html5炫酷动画及源码,分享8个难忘的HTML5炫酷动画及源码
  10. 兄弟连php课件,兄弟连php课件