堆排序

在学习了二叉堆(优先队列)以后,我们来看看堆排序。堆排序总的运行时间为O(NlonN)。

堆的概念

堆是以数组作为存储结构。

可以看出,它们满足以下规律

设当前元素在数组中以R[i]表示,那么(下标从0开始),

(1) 它的左孩子结点是:R[2*i+1];

(2) 它的右孩子结点是:R[2*i+2];

(3) 它的父结点是:R[(i-1)/2];

(4) R[i] <= R[2*i+1] 且 R[i] <= R[2i+2]。

(5)最后一个父节点是N/2-1;(构建堆要从这里开始下滤)

要点

首先,按堆的定义将数组R[0..n]调整为大根堆(这个过程称为创建初始堆),交换R[0]和R[n],这样最大值去了数组的最后一个;

然后,将R[0..n-1]调整为堆,交换R[0]和R[n-1];

如此反复,直到交换了R[0]和R[1]为止。

以上思想可归纳为两个操作:

(1)根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。

(2)每次交换第一个和最后一个元素,输出最后一个元素(将它放到数组最后)(最大值),然后把剩下元素重新调整为大根堆。

当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。

先通过详细的实例图来看一下,如何构建初始堆

构建初始堆,从最后一个父节点开始往数组前面遍历,遍历所有父节点,将这些父节点对应的子树构成局部大根堆,将孩子中较大的节点放到父节点处,父节点下滤到孩子处,然后继续和下面的孩子比较,继续下滤。

设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }。

构造了初始堆后,我们来看一下完整的堆排序处理:

还是针对前面提到的无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 来加以说明。

相信,通过以上两幅图,应该能很直观的演示堆排序的操作处理。

构建大(小)根堆的下滤:选择孩子节点中较大(小)的节点取代父节点。。

核心代码...(数组从0开始填数据)

 //构建的是大根堆
public void HeapAdjust(int[] array, int parent, int length) {
    int temp = array[parent]; // temp保存当前父节点
    int child = 2 * parent + 1; // 先获得左孩子
 
    while (child < length) {
        // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
        if (child != length-1 && array[child] < array[child + 1]) {
            child++;
        }
 
        // 如果父结点的值已经大于孩子结点的值,则直接结束
        if (temp >= array[child])
            break;
 
        // 父节点小于孩子节点,把孩子结点的值赋给父结点
        array[parent] = array[child];
 
        // 选取孩子结点的左孩子结点,继续向下筛选
        parent = child;
        child = 2 * child + 1;
    }
 
    array[parent] = temp;
}
 
public void heapSort(int[] list) {
    // 从最后一个父节点开始循环建立初始堆
    /*
  构建堆的时候,从最后一个父节点开始,每个父节点和自己的左右子树比较,将自己这一子树(从自己到叶子)构建成大根堆。每个父节点下滤时,不用考虑他的父节点和兄弟节点,只需考虑孩子即可。
*/
    for (int i = list.length / 2-1; i >= 0; i--) {
        HeapAdjust(list, i, list.length);
    }
 
    // 进行n-1次循环,完成排序
    for (int i = list.length - 1; i > 0; i--) {
        // 最后一个元素和第一元素进行交换
        int temp = list[i];
        list[i] = list[0];
        list[0] = temp;
 
        // 筛选 R[0] 结点,得到i-1个结点的堆
        HeapAdjust(list, 0, i);
        
    }
}
 

算法分析

堆排序算法的总体情况

排序类别

排序方法

时间复杂度

空间复杂度

稳定性

复杂性

平均情况

最坏情况

最好情况

选择排序

堆排序

O(nlog2n)

O(nlog2n)

O(nlog2n)

O(1)

不稳定

较复杂

时间复杂度

堆的存储表示是顺序的。因为堆所对应的二叉树为完全二叉树,而完全二叉树通常采用顺序存储方式。

当想得到一个序列中第k个最小的元素之前的部分排序序列,最好采用堆排序。

因为堆排序的时间复杂度是O(n+klog2n),若k≤n/log2n,则可得到的时间复杂度为O(n)

算法稳定性

堆排序是一种不稳定的排序方法。

因为在堆的调整过程中,关键字进行比较和交换所走的是该结点到叶子结点的一条路径,

因此对于相同的关键字就可能出现排在后面的关键字被交换到前面来的情况。

转载:https://www.cnblogs.com/jingmoxukong/p/4303826.html

排序算法入门之堆排序(Java实现)的更多相关文章

  1. 数据结构和算法(Golang实现)(24)排序算法-优先队列及堆排序

    优先队列及堆排序 堆排序(Heap Sort)由威尔士-加拿大计算机科学家J. W. J. Williams在1964年发明,它利用了二叉堆(A binary heap)的性质实现了排序,并证明了二叉 ...

  2. 常用的排序算法介绍和在JAVA的实现(二)

    一.写随笔的原因:本文接上次的常用的排序算法介绍和在JAVA的实现(一) 二.具体的内容: 3.交换排序 交换排序:通过交换元素之间的位置来实现排序. 交换排序又可细分为:冒泡排序,快速排序 (1)冒 ...

  3. 排序算法入门之快速排序(java实现)

    快速排序也是一种分治的排序算法.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序,会需要一个额外的数组:而快速排序的排序方式是当两个子数组都有序时 ...

  4. 各种排序算法的分析及java实现

    排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强能写出个冒泡排序.由于下半年要准备工作了,也知道排序算法的重要性(据说是面试必问的知识点),所以又花了点时间 ...

  5. (转)各种排序算法的分析及java实现

    转自:http://www.cnblogs.com/liuling/p/2013-7-24-01.html 排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强 ...

  6. 各种排序算法的分析及java实现 分类: B10_计算机基础 2015-02-03 20:09 186人阅读 评论(0) 收藏

    转载自:http://www.cnblogs.com/liuling/p/2013-7-24-01.html 另可参考:http://gengning938.blog.163.com/blog/sta ...

  7. 常用的排序算法介绍和在JAVA的实现(一)

    一.写随笔的原因:排序比较常用,借此文介绍下排序常用的算法及实现,借此来MARK一下,方便以后的复习.(本人总是忘得比较快) 二.具体的内容: 1.插入排序 插入排序:在前面已经排好序的序列中找到合适 ...

  8. 排序算法三:堆排序(Heapsort)

    堆排序(Heapsort)是一种利用数据结构中的堆进行排序的算法,分为构建初始堆,减小堆的元素个数,调整堆共3步. (一)算法实现 protected void sort(int[] toSort) ...

  9. 排序算法之直接插入排序Java实现

    排序算法之直接插入排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb  选择排序:http://t.cn/hros6e  插入排序: ...

随机推荐

  1. 理解WebKit和Chromium: Chromium WebView和Chrome浏览器渲染机制

    转载请注明原文地址:http://blog.csdn.net/milado_nju ## 数据对比 前面介绍过Chromium WebView的时候,说过有关ChromiumWebView同Chrom ...

  2. java虚拟机工具入门

    jps 能显示现在都有那些java程序运行 C:\Users\Administrator>jps 16964 DeadLockJstack 9172 PULSEI~1.JAR 19392 Jps ...

  3. Swift基础之实现一个镂空图片的小Demo

    前两天看了别人的文章,涉及到了镂空的展示,所以我在这里把实现的内容写成Swift语言的小Demo,供大家欣赏 首先,需要创建导航视图,然后创建两种展示方式的按钮 let vc = ViewContro ...

  4. 任务定义器——SocketProcessor

    将socket扔进线程池前需要定义好任务,要进行哪些逻辑处理由SocketProcessor定义,根据线程池的约定,作为任务必须扩展Runnable.用如下伪代码表示 protected class ...

  5. 使用Hash函数和MAC产生伪随机数

    基于Hash函数的PRNG 流程非常类似于对称密码的CTR工作模式 算法的伪码如下 m = ⌈n/outlen⌉ data = V W = the null String for i = 1 to m ...

  6. 03 ProgressBar 进度条

    >      style="?android:attr/progressBarStyleSmall" 样式                 android:progress= ...

  7. pyinstaller使用-python项目转换成exe可执行文件

    http://blog.csdn.net/pipisorry/article/details/50620122 Python不是每个人的计算机里面都有安装,当您写了一个好用的工具,需要一个standa ...

  8. UNIX环境高级编程——信号基本概述和signal函数

    一.为了理解信号,先从我们最熟悉的场景说起:1. 用户输入命令,在Shell下启动一个前台进程.2. 用户按下Ctrl-C,这个键盘输入产生一个硬件中断.3. 如果CPU当前正在执行这个进程的代码,则 ...

  9. EBS条形码打印

    Oracle  提供两种方式实现 128 码的编码 第一种方式是使用 Reports Builder 实现对 128 码编码, 在 Metalink 305090.1[1]  有 比较详尽的描述,其中 ...

  10. 【一天一道LeetCode】#33. Search in Rotated Sorted Array

    一天一道LeetCode 本系列文章已全部上传至我的github,地址: https://github.com/Zeecoders/LeetCode 欢迎转载,转载请注明出处 (一)题目 Suppos ...