博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
常用内部排序算法之三:堆排序
阅读量:6244 次
发布时间:2019-06-22

本文共 2187 字,大约阅读时间需要 7 分钟。

前言

堆排序是以堆为原型的排序。堆首先是一棵二叉树,具有以下两个性质:每个节点的值大于或者等于其左右孩子结点的值,称为大顶堆;或者每个节点的值都小于或者等于其左右孩子结点的值,称为小顶堆。从这个定义中可以发现,堆得根节点要么是最大值要么是最小值。实现堆排序的基本思想是:将待排序的序列构造成一个大顶堆或者小顶堆。此时整个堆满足根节点是最大值或者最小值。将根节点移走,并与堆数组的最后一个值进行交换,这样的话最后一个值就是最大值或者最小值了。然后将剩余n-1(假设原来总共有n个元素)未排序的序列重新构造成一个最大堆或者最小堆,再与除原来最大值之外的最后一个元素进行交换,得到次小值。如此反复进行,就得到一个排序的序列。

堆排序算法的Java实现

package com.rhwayfun.algorithm.sort;public class HeapSort2 {    public void heapSort(int[] a){        for(int i = a.length/2-1; i >= 0; i--){            //建立一个最大堆            heapAdjust(a,i,a.length - 1);        }        for (int i = a.length - 1; i > 0; i--) {            //将堆顶元素与当前未经排序的最后一个记录交换            swap(a,0,i);            //将数组a中下标从0到i-1的子序列重新调整为最大堆            heapAdjust(a, 0, i - 1);        }        for (int i = 0; i < a.length; i++) {            System.out.println(a[i]);        }    }    private void swap(int[] a, int i, int j) {        int temp = a[i];        a[i] = a[j];        a[j] = temp;    }    private void heapAdjust(int[] a, int s, int m) {        int temp = 0,j=0;        //把堆顶元素保存在临时变量中        temp = a[s];        //由于s可能是0,所以需要+1。此外,如果当前结点的序号是s,那么其左孩子是2*s+1(+1是因为s可能是0)        for(j = 2*s+1; j <= m; j = 2*j+1){            //找出左右孩子较大的结点的下标            if(j < m && a[j] < a[j+1]){                ++j;            }            if(temp >= a[j]){                break;            }            //把较大的孩子结点的赋给当前结点            a[s] = a[j];            //把更大孩子结点的下标赋给s            s = j;        }        //把原来s下标位置的值赋给新的下标为s的值,这样就完成了当前结点与更大孩子结点值的交换        a[s] = temp;    }    public static void main(String[] args) {        new HeapSort2().heapSort(new int[]{
90,70,80,60,10,40,50,30,20}); }}

可以发现在建立最大堆的时候,i是从a.length/2开始的,为什么要从这个位置开始呢?比如有一个数组{

90,70,80,60,10,40,50,30,20},初始情况是这样的:

一共有9个元素,那么a.length/21的值是3,也就是第4个元素,执行for循环,i的值从3->2->1->0变化,可以发现这几个下标的结点都是有孩子结点的元素,所以第一个for循环就很好理解了:其实就是从下往上、从右到左,将每个非终端结点当做根节点,将其和子树调整成最大堆,所以下标3->2->1->0的变化,也就是60,80,70,90结点的调整过程。

堆排序算法的时间复杂度

可以发现堆排序的主要时间主要花在建堆和重建堆时的反复筛选上。在初始建堆的时候,由于只涉及两个节点的交换操作,所以建堆的时间复杂度是O(n)。在正式排序的时候,第i次取堆顶的元素并重建堆需要花去O(logi)的时间,需要n-1次取堆顶记录,所以排序的过程的时间复杂度是O(nlogn)。而且这是最好、最坏和平均的时间复杂度,但是由于记录的交换与比较是跳跃式的,所以与归并排序不同,堆排序是一种不稳定的排序算法。

由于初始建堆的比较次数比较多,所以堆排序不适合记录数较少的情况(使用简单排序算法是一种不错的选择,没必要大材小用了)。

你可能感兴趣的文章
hdu 1071 The area
查看>>
char,short ,int ,long,long long,unsigned long long数据范围
查看>>
ffmpeg处理rtmp/文件/rtsp的推流和拉流
查看>>
jquery13 attr() prop() val() addClass()等 : 对元素属性的操作
查看>>
UVa 263 - Number Chains
查看>>
设计模式之模板方法模式
查看>>
在 Windows Server 2008 中部署带 SignalR 的网站出错
查看>>
A glance for agile method
查看>>
Java高级教程:Java并发性和多线程
查看>>
Android更新带进度条的通知栏
查看>>
Python XML解析
查看>>
五步搭建属于自己的个人网站
查看>>
换今日特价图片---轻开电子商务系统(企业入门级B2C站点)
查看>>
任务调度利器:Celery
查看>>
利用java mail发送邮件(转)
查看>>
Mybatis(六) Spring整合mybatis
查看>>
教您用Xshell快速连接远程电脑
查看>>
怎样阅读源码
查看>>
RxJava系列之中的一个 初识Rxjava
查看>>
智能巡检资料
查看>>