堆排序

在学习了二叉堆(优先队列)以后,我们来看看堆排序。堆排序总的运行时间为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. linux qcom LCD framwork

    点击打开链接 0.关键字 MDSS : Multimedia Display sub system DSI: Display Serial Interface 1.涉及文件 (1) drivers\v ...

  2. UNIX网络编程——设置套接字超时

    在涉及套接字的I/O操作上设置超时的方法有以下3种: 调用alarm,它在指定超时期时产生SIGALRM信号.这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm ...

  3. C语言--测试电脑存储模式(大端存储OR小端存储)

    相信大家都知道大端存储和小端存储的概念,这在平时,我们一般不用考虑,但是,在某些场合,这些概念就显得很重要,比如,在 Socket 通信时,我们的电脑是小端存储模式,可是传送数据或者消息给对方电脑时, ...

  4. Android面试题总结

    1.Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念 DVM指dalivk的虚拟机.每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟 ...

  5. 择天记OL体验截图

  6. C++编译器对属性和方法的处理机制

    C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类.从计算机的角度,程序依然由数据段和代码段构成. C++编译器如何完成面向对象理论到计算机程序 ...

  7. Git版本控制:Git高级教程

    http://blog.csdn.net/pipisorry/article/details/50669350 Git有很多命令行参数,使用起来非常方便.可以运行 man git log ,来看一下这 ...

  8. android dataBinding详解

    官方介绍地址:http://developer.android.com/intl/zh-cn/tools/data-binding/guide.html 2015 Google IO 大会带来的 Da ...

  9. 【shell脚本练习】批量添加用户和设置密码

    题目 添加9个用户,user101-user109:密码同用户名: 思路 for循环来添加就好了,用户名和密码都可以拼字符串来完成 user10+数字 要判断是否能添加成功,注意非交互模式下修改用户密 ...

  10. Let the Balloon Rise HDU 1004

    Let the Balloon Rise Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other ...