堆是一种特殊的完全二叉树,其特点是所有父节点都比子节点要小,或者所有父节点都比字节点要大。前一种称为最小堆,后一种称为最大堆。

比如下面这两个:

那么这个特性有什么作用?既然题目是堆排序,那么肯定能用来排序。想要用堆排序首先要创建一个堆,如果对4 3 6 2 7 1 5这七个数字做从小到大排序,需要用这七个数创建一个最大堆,来看代码:

  1. public class HeapSort {
  2. private int[] numbers;
  3. private int length;
  4. public HeapSort(int[] numbers) {
  5. this.numbers = numbers;
  6. this.length = numbers.length;
  7. }
  8. /**
  9. * 调整二叉树
  10. * 如果父节点编号为x, 那么左子节点的编号是2x, 右子节点的编号是2x+1
  11. * 节点编号从1开始, 对应数组中的索引是编号-1
  12. * @param nodeId 节点编号, 从1开始
  13. */
  14. public void adjust(int nodeId) {
  15. int swapId;
  16. int flag = 0; //是否需要继续向下调整
  17. while(nodeId * 2 <= this.length && flag == 0) {
  18. //首先判断它和左子节点的关系, 并用swapId记录值较小的节点编号(最大堆是记录较大的)
  19. int index = nodeId - 1; //节点对应数组中数字的索引
  20. int leftChild = nodeId * 2 - 1; //左子节点对应数组中数字的索引
  21. int rightChild = nodeId * 2; //右子节点对应数组中数字的索引
  22. if(numbers[index] < numbers[leftChild])  {
  23. swapId = nodeId * 2;
  24. } else {
  25. swapId = nodeId;
  26. }
  27. //如果有右子节点, 再与右子节点比较
  28. if(nodeId * 2 + 1 <= this.length) {
  29. if(numbers[swapId - 1] < numbers[rightChild])
  30. swapId = nodeId * 2 + 1;
  31. }
  32. //如果最小的节点编号不是自己, 说明子节点中有比父节点更小的
  33. if(swapId != nodeId) {
  34. swap(swapId, nodeId);
  35. nodeId = swapId;
  36. } else {
  37. flag = 1;
  38. }
  39. }
  40. }
  41. /**
  42. * 交换两个节点的值
  43. * @param nodeId1
  44. * @param nodeId2
  45. */
  46. public void swap(int nodeId1, int nodeId2) {
  47. int t = numbers[nodeId1 - 1];
  48. numbers[nodeId1 - 1] = numbers[nodeId2 - 1];
  49. numbers[nodeId2 - 1] = t;
  50. }
  51. /**
  52. * 创建最大堆
  53. */
  54. public void createMaxHeap() {
  55. //从最后一个非叶节点到第一个节点依次向上调整
  56. for(int i = this.length / 2; i >= 1; i--) {
  57. adjust(i);
  58. }
  59. }
  60. public static void main(String[] args) {
  61. int[] numbers = new int[] { 4, 3, 6, 2, 7, 1, 5 };
  62. for(int x = 0; x < numbers.length; x++) {
  63. System.out.print(numbers[x] + "  ");
  64. }
  65. System.out.println();
  66. HeapSort heap = new HeapSort(numbers);
  67. heap.createMaxHeap();
  68. }
  69. }

对本例中的数列,从this.length / 2到1,共执行了三轮循环。

第一轮:

第二轮:


第三轮:

调整完成后,当前的二叉树已经符合最大堆的特性,可以用来从小到大排序。堆排序的原理是,交换堆顶和最后一个节点的数字,即把最大的数字放到数组最后,然后对除了最大数的前n-1个数从新执行调整过程,使其符合最大堆特性。重复以上过程直到堆中只剩下一个数字。

  1. public void sort() {
  2. while(this.length > 1) {
  3. swap(1, this.length);
  4. this.length--;
  5. adjust(1);
  6. }
  7. for(int x = 0; x < numbers.length; x++) {
  8. System.out.print(numbers[x] + "  ");
  9. }
  10. }

堆排序的时间复杂度和快速排序的平均时间复杂度一样,是O(nlogn)。

Java与算法之(8) - 堆排序的更多相关文章

  1. java排序算法(三):堆排序

    java排序算法(三)堆排序 堆积排序(HeapSort)是指利用堆积树这种结构所设计的排序算法,可以利用数组的特点快速定位指定索引的元素.堆排序是不稳定的排序方法.辅助空间为O(1).最坏时间复杂度 ...

  2. java排序算法(一):概述

    java排序算法(一)概述 排序是程序开发中一种非常常见的操作,对一组任意的数据元素(活记录)经过排序操作后,就可以把它们变成一组按关键字排序的一组有序序列 对一个排序的算法来说,一般从下面三个方面来 ...

  3. java排序算法(四):冒泡排序

    java排序算法(四):冒泡排序 冒泡排序是计算机的一种排序方法,它的时间复杂度是o(n^2),虽然不及堆排序.快速排序o(nlogn,底数为2).但是有两个优点 1.编程复杂度很低.很容易写出代码 ...

  4. Java排序算法(三)

    Java排序算法(三) 三.Java排序算法总结 从这三组时间复杂度对比中,可以看出,堆排序和归并排序是不管在什么情况下发挥稳定的,快速排序好的时候表现如天才,坏情况下比较差强人意,甚至在等待排序个数 ...

  5. Java排序算法(一)

    Java排序算法(一) 排序的基本概念和分类 1.1排序的定义 在<大话数据结构>中,排序定义为,假设含有n个记录的序列为{r1,r2,...,rn},其相应的关键字{k1,k2,..., ...

  6. java排序算法之冒泡排序和快速排序

    总结一下Java排序算法,以便记忆. 各类排序的时间复杂度: 排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性 复杂性 直接插入排序 O(n2)O(n2) O( ...

  7. 史上最全的java随机数生成算法分享(转)

    这篇文章主要介绍了史上最全的java随机数生成算法,我分享一个最全的随机数的生成算法,最代码的找回密码的随机数就是用的这个方法 String password = RandomUtil.generat ...

  8. 常用Java排序算法

    常用Java排序算法 冒泡排序 .选择排序.快速排序 package com.javaee.corejava; public class DataSort { public DataSort() { ...

  9. 使用Java练习算法常用的基本操作

    一.使用Java练习算法常常需要使用控制台的数据输入和输出,下面记录一下基本的使用方法: 基本用法 import java.util.*; public class Main { public sta ...

随机推荐

  1. 小强的Hadoop学习之路

    本人一直在做NET开发,接触这行有6年了吧.毕业也快四年了(6年是因为大学就开始在一家小公司做门户网站,哈哈哈),之前一直秉承着学要精,就一直一门心思的在做NET(也是懒吧).最近的工作一直都和大数据 ...

  2. DotNetCasClient 如何获取Cas服务器返回的attributes中的数据

    最近开始接触做与其它认证系统的集成,其中有个是与某学校的CAS服务器集成.cas服务器认证成功后返回的数据格式如下: 其中红色部分是我需要取出来用于识别用户身份的数据. 一开始,我根据网上的教程,引用 ...

  3. C# 委托高级应用----线程——创建无阻塞的异步调用(一)

    前言 本文大部分内容来自于mikeperetz的Asynchronous Method Invocation及本人的一些个人体会所得,希望对你有所帮助.原英文文献可以在codeproject中搜索到. ...

  4. iOS常见的几种加密方法(base64.MD5.Token传值.系统指纹验证。。加密)

    普通加密方法是讲密码进行加密后保存到用户偏好设置中 钥匙串是以明文形式保存,但是不知道存放的具体位置 一. base64加密 base64 编码是现代密码学的基础 基本原理: 原本是 8个bit 一组 ...

  5. Java I/O---获取文件目录并写入到文本

    首先获取指定目录下的所有文件目录,存入List集合中,然后创建文本文件将List遍历写入文本中保存. 1.主程序类 public class Test { /** * @param args */ p ...

  6. 小白的Python之路 day4 迭代器

    迭代器 学习前,我们回想一下可以直接作用于for循环的数据类型有以下几种: 1.集合数据类型,如list.tuple.dict.set.str等: 2.是generator,包括生成器和带yield的 ...

  7. 530. Minimum Absolute Difference in BST

    Given a binary search tree with non-negative values, find the minimum absolute difference between va ...

  8. splay小结—植树结

    我要把高级数据结构当爸爸了... ...弱到跪烂了. splay,二叉搜索树的一种,具有稳定变形功能. 二叉搜索树:对于一个节点,都只有不超过2个孩子.其左子树内的点的权值都比这个点小,右子树的点的权 ...

  9. Java 浮点型与双精度数值比较

    对于双精度与浮点数之间的比较存在潜在的转化

  10. SpringBoot_02_servlet容器配置

    二.参考资料 1.Spring boot 自定义端口 2.Spring Boot的Web配置(九):Tomcat配置和Tomcat替换