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

比如下面这两个:

那么这个特性有什么作用?既然题目是堆排序,那么肯定能用来排序。想要用堆排序首先要创建一个堆,如果对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. 【java】文件复制的简单实现

    package 文件操作; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; ...

  2. Hibernate学习---单表查询

    我们都知道SQL是非常强大的,为什么这么说呢?相信学过数据库原理的同学们都深有体会,SQL语句变化无穷,好毫不夸张的说可以实现任意符合我们需要的数据库操作,既然前面讲到Hibernate非常强大,所以 ...

  3. shell按行读取文件

    这工作小半年了发现以前学的那么多流弊技能都不怎么用,倒是shell用的很多,自己已经从shell小菜鸟一步步走过来,已经要变成大菜鸟=.= 经常需要用shell按行读取配置文件,自己在上面踩了很多坑, ...

  4. ArcGIS API for JavaScript 4.2学习笔记[7] 鹰眼(缩略图的实现及异步处理、Promise、回调函数、监听的笔记)

    文前说明:关于style就是页面的css暂时不做评论,因为官方给的例子的样式实在太简单了,照抄阅读即可. 这篇文章有着大量AJS 4.x版本添加的内容,如监听watch.Promise对象.回调函数. ...

  5. bzoj 4813: [Cqoi2017]小Q的棋盘

    Description 小Q正在设计一种棋类游戏.在小Q设计的游戏中,棋子可以放在棋盘上的格点中.某些格点之间有连线,棋子只能 在有连线的格点之间移动.整个棋盘上共有V个格点,编号为0,1,2-,V- ...

  6. ES6 let和const命令(4)

    const声明的常量只在当前代码块有效.如果想设置跨模块的常量,可以采用下面的写法. //constants.js模块 export const A = 1; export const B = 3; ...

  7. Less is exponentially more

    Less is exponentially more  (原文出处:rob pike 博客,https://commandcenter.blogspot.jp/2012/06/less-is-expo ...

  8. Android 执行 adb shell 命令

    Android 执行Adb shell 命令大多需要root权限,Android自带的Runtime. getRuntime().exec()容易出错,在网上找到了一个执行adb shell命令的类 ...

  9. 总结oninput、onchange与onpropertychange事件的用法和区别

    前端页面开发的很多情况下都需要实时监听文本框输入,比如腾讯微博编写140字的微博时输入框hu9i动态显示还可以输入的字数.过去一般都使用onchange/onkeyup/onkeypress/onke ...

  10. HyperV下安装Centos 7全屏显示方法

    Hyper-v一般模式的分辨率很小,所以我们在电脑上显示的时候往往不能全屏,即使全屏了也只是轮廓全部工作区并没有全屏显示.导致这个问题的原因是:我们在装系统时,没有选择合适的屏幕分辨率,所以这里只要在 ...