详解堆排序算法原理及Java版的代码实现_java

概述
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义如下:具有n个元素的序列(k1,k2,...,kn), 当且仅当满足:

时称之为堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(小顶堆)或最大项(大顶堆)。
若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点(有子女的结点)的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。
(a)大顶堆序列:(96, 83, 27, 38, 11, 09)
(b)小顶堆序列:(12, 36, 24, 85, 47, 30, 53, 91)

初始时把要排序的n 个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素。然后对剩下的n-1个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到最后得到有n个节点的有序序列。称这个过程为堆排序。

步骤&实例
实现堆排序需解决两个问题:
(1)如何将n 个待排序的数建成堆;
(2)输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。
建堆方法(小顶堆):
对初始序列建堆的过程,就是一个反复进行筛选的过程。
n 个结点的完全二叉树,则最后一个结点是第n/2个结点的子树。
筛选从第n/2个结点为根的子树开始(n/2是最后一个有子树的结点),使该子树成为堆。
之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
如图建堆初始过程
无序序列:(49, 38, 65, 97, 76, 13, 27, 49)

(a) 无序序列,初始二叉树,97(第8/2=4个结点)为最后一个结点(49)的父结点。
(b) 97>=49,替换位置,接下来对n/2的上一个结点65进行筛选。
(c) 13<=27且65>=13,替换65和13的位置,接下来对38进行替换(都大于它,不需操作),对49进行筛选。
(d) 13<=38且49>=13,替换49和13的位置,49>=27,替换49和27的位置。
(e) 最终得到一个堆,13是我们得到的最小数。
调整堆的方法(小顶堆):
设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶,堆被破坏,其原因仅是根结点不满足堆的性质。
将根结点与左、右子树中较小元素的进行交换。
若与左子树交换:如果左子树堆被破坏,则重复方法(2).
若与右子树交换,如果右子树堆被破坏,则重复方法(2).
继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。
调整堆只需考虑被破坏的结点,其他的结点不需调整。

代码实现(Java)
运行代码结合注释与上面的实例步骤进行对比思考。

package com.coder4j.main;

public class HeapSort {

  /**
   * 调整为小顶堆(排序后结果为从大到小)
   *
   * @param array是待调整的堆数组
   * @param s是待调整的数组元素的位置
   * @param length是数组的长度
   *
   */
  public static void heapAdjustS(int[] array, int s, int length) {
    int tmp = array[s];
    int child = 2 * s + 1;// 左孩子结点的位置
    System.out.println("待调整结点为:array[" + s + "] = " + tmp);
    while (child < length) {
      // child + 1 是当前调整结点的右孩子
      // 如果有右孩子且小于左孩子,使用右孩子与结点进行比较,否则使用左孩子
      if (child + 1 < length && array[child] > array[child + 1]) {
        child++;
      }
      System.out.println("将与子孩子 array[" + child + "] = " + array[child] + " 进行比较");
      // 如果较小的子孩子比此结点小
      if (array[s] > array[child]) {
        System.out.println("子孩子比其小,交换位置");
        array[s] = array[child];// 把较小的子孩子向上移动,替换当前待调整结点
        s = child;// 待调整结点移动到较小子孩子原来的位置
        array[child] = tmp;
        child = 2 * s + 1;// 继续判断待调整结点是否需要继续调整

        if (child >= length) {
          System.out.println("没有子孩子了,调整结束");
        } else {
          System.out.println("继续与新的子孩子进行比较");
        }
        // continue;
      } else {
        System.out.println("子孩子均比其大,调整结束");
        break;// 当前待调整结点小于它的左右孩子,不需调整,直接退出
      }
    }
  }

  /**
   * 调整为大顶堆(排序后结果为从小到大)
   *
   * @param array是待调整的堆数组
   * @param s是待调整的数组元素的位置
   * @param length是数组的长度
   *
   */
  public static void heapAdjustB(int[] array, int s, int length) {
    int tmp = array[s];
    int child = 2 * s + 1;// 左孩子结点的位置
    System.out.println("待调整结点为:array[" + s + "] = " + tmp);
    while (child < length) {
      // child + 1 是当前调整结点的右孩子
      // 如果有右孩子且大于左孩子,使用右孩子与结点进行比较,否则使用左孩子
      if (child + 1 < length && array[child] < array[child + 1]) {
        child++;
      }
      System.out.println("将与子孩子 array[" + child + "] = " + array[child] + " 进行比较");
      // 如果较大的子孩子比此结点大
      if (array[s] < array[child]) {
        System.out.println("子孩子比其大,交换位置");
        array[s] = array[child];// 把较大的子孩子向上移动,替换当前待调整结点
        s = child;// 待调整结点移动到较大子孩子原来的位置
        array[child] = tmp;
        child = 2 * s + 1;// 继续判断待调整结点是否需要继续调整

        if (child >= length) {
          System.out.println("没有子孩子了,调整结束");
        } else {
          System.out.println("继续与新的子孩子进行比较");
        }
        // continue;
      } else {
        System.out.println("子孩子均比其小,调整结束");
        break;// 当前待调整结点大于它的左右孩子,不需调整,直接退出
      }
    }
  }

  /**
   * 堆排序算法
   *
   * @param array
   * @param inverse true 为倒序排列,false 为正序排列
   */
  public static void heapSort(int[] array, boolean inverse) {
    // 初始堆
    // 最后一个有孩子的结点位置 i = (length - 1) / 2, 以此向上调整各结点使其符合堆
    System.out.println("初始堆开始");
    for (int i = (array.length - 1) / 2; i >= 0; i--) {
      if (inverse) {
        heapAdjustS(array, i, array.length);
      } else {
        heapAdjustB(array, i, array.length);
      }
    }
    System.out.println("初始堆结束");
    for (int i = array.length - 1; i > 0; i--) {
      // 交换堆顶元素H[0]和堆中最后一个元素
      int tmp = array[i];
      array[i] = array[0];
      array[0] = tmp;
      // 每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
      if (inverse) {
        heapAdjustS(array, 0, i);
      } else {
        heapAdjustB(array, 0, i);
      }
    }
  }

  public static void main(String[] args) {
    int[] array = { 49, 38, 65, 97, 76, 13, 27, 49 };
    heapSort(array, false);
    for (int i : array) {
      System.out.print(i + " ");
    }
  }

}

运行结果:

初始堆开始
待调整结点为:array[3] = 97
将与子孩子 array[7] = 49 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[2] = 65
将与子孩子 array[5] = 13 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[1] = 38
将与子孩子 array[3] = 49 进行比较
子孩子均比其大,调整结束
待调整结点为:array[0] = 49
将与子孩子 array[2] = 13 进行比较
子孩子比其小,交换位置
继续与新的子孩子进行比较
将与子孩子 array[6] = 27 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
初始堆结束
待调整结点为:array[0] = 97
将与子孩子 array[2] = 27 进行比较
子孩子比其小,交换位置
继续与新的子孩子进行比较
将与子孩子 array[6] = 49 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[0] = 97
将与子孩子 array[1] = 38 进行比较
子孩子比其小,交换位置
继续与新的子孩子进行比较
将与子孩子 array[3] = 49 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[0] = 65
将与子孩子 array[1] = 49 进行比较
子孩子比其小,交换位置
继续与新的子孩子进行比较
将与子孩子 array[4] = 76 进行比较
子孩子均比其大,调整结束
待调整结点为:array[0] = 76
将与子孩子 array[2] = 49 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[0] = 97
将与子孩子 array[1] = 65 进行比较
子孩子比其小,交换位置
没有子孩子了,调整结束
待调整结点为:array[0] = 76
将与子孩子 array[1] = 97 进行比较
子孩子均比其大,调整结束
待调整结点为:array[0] = 97
97 76 65 49 49 38 27 13 

PS:堆排序与直接插入排序的区别
直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, 排序
, 堆
, 堆排序
排序算法
堆排序原理及算法实现、堆排序算法实现、fft算法c语言实现详解、aes加密算法原理详解、mallat算法c实现详解,以便于您获取更多的相关知识。

时间: 2024-08-31 07:33:37

详解堆排序算法原理及Java版的代码实现_java的相关文章

javaScript中的this示例学习详解及工作原理

 这篇文章主要介绍了javaScript中的this示例学习详解及工作原理,大家参考使用吧 this的工作原理   如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象.   代码如下: var parent = {     method: function () {         console.log(this);     } };   parent.method(); // <- parent       注意这种行为非常"脆弱",如果你获取一个方法的引用

详解快速排序算法中的区间划分法及Java实现示例_java

快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.该方法的基本思想是: 1.先从数列中取出一个数作为基准数. 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边. 3.再对左右区间重复第二步,直到各区间只有一个数. 算法的思路很清晰,但是如果在区间划分过程中边界值没有处理好,也是很容易出现bug的.下面给出两种比较清晰的思维来指导区间划分代码的编写. 第一种思维即所谓的挖坑法思维,下面通过分析一个实例来分析一下挖坑法的过程: 以一个数组作为示例,取区间

Java虚拟机详解04----GC算法和种类【重要】

本文主要内容: GC的概念 GC算法  引用计数法(无法解决循环引用的问题,不被java采纳)     根搜索算法     现代虚拟机中的垃圾搜集算法: 标记-清除 复制算法(新生代) 标记-压缩(老年代)     分代收集 Stop-The-World   一.GC的概念: GC:Garbage Collection 垃圾收集 1960年 Lisp使用了GC Java中,GC的对象是Java堆和方法区(即永久区) 我们接下来对上面的三句话进行一一的解释: (1)GC:Garbage Colle

《Java和Android开发实战详解》——1.2节Java基础知识

1.2 Java基础知识 Java和Android开发实战详解 Java语言类似于C++是一种编译型语言,不过两者并不完全相同,严格说来,Java是结合编译和解释优点的一种编程语言. 1.2.1 Java平台 "平台"(Platform)是一种结合硬件和软件的执行环境.Java既是一种高级的面向对象的编程语言,也是一个平台.Java平台是一种纯软件平台,它可以在各种基于硬件的平台上运行,与硬件无关,主要是由JVM和Java API两个部分组成. 1.JVM虚拟机 JVM(Java Vi

详解DES加密算法及在Java程序中的使用示例_java

DES加密算法DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1976年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),随后在国际上广泛流传开来. DES算法的入口参数有三个:Key.Data.Mode.其中Key为7个字节共56位,是DES算法的工作密钥;Data为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密. DES算法把64位的明文输入块变为64位的密文输出块,它所使用的密钥

《Java和Android开发实战详解》——1.3节Java语言的开发环境

1.3 Java语言的开发环境 Java和Android开发实战详解 编程语言的"开发环境"(Development Environment)指的是一组工具程序,可用来创建.编译和维护编程语言所构建的应用程序.一般来说,我们可以使用两种Java开发环境来创建Java应用程序. 1.终端机模式的开发环境 或称为"命令行模式",对于传统MS-DOS或UNIX.Linux系统的用户,程序执行时的输入数据和输出数据都是使用"命令行界面"(Command-

字符串的模式匹配详解--BF算法与KMP算法_C 语言

一.BF算法    BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符:若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果.    举例说明: S: ababcababa P: ababa BF算法匹配的步骤如下 i=0 i=1 i=2 i=3 i=4 第一趟:ababcababa 第二趟:ababcababa 第三趟:ababcababa 第四趟:ababcab

Linux : select()详解 和 实现原理【转】

转自:http://blog.csdn.net/huntinux/article/details/39289317 原文:http://blog.csdn.net/boboiask/article/details/4055655 Linux-select详解   select系统调用时用来让我们的程序监视多个文件句柄的状态变化的.程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变. 关于文件句柄,其实就是一个整数,通过socket函数的声明就明白了: int sock

详解MyBatis直接执行SQL查询及数据批量插入_java

一.直接执行SQL查询: 1.mappers文件节选 <resultMap id="AcModelResultMap" type="com.izumi.InstanceModel"> <result column="instanceid" property="instanceID" jdbcType="VARCHAR" /> <result column="insta