排序和顺序统计量之堆排序

比较排序算法

我们平时接触到的一些重要的排序算法,插入排序、归并排序、堆排序及快速排序都属于比较排序算法(即都是通过对元素进行比较操作来确定输入数组的次序)。已经证明了任意比较排序算法排序 n 个元素的最坏情况运行时间的下界为Ω(nlgn)(使用决策树模型),从而说明了堆排序和归并排序是渐进最优的比较排序算法。

堆排序

(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树。除了最底层,该树是完全充满的,而且是从左向右充满。
堆的根结点是 A[1] ,给定一个结点 i ,他的父结点为i/2的下取整,他的左孩子结点为 2i ,右孩子结点为 2i+1
但是由于Java中数组的下标是从 0 开始直到n1,那么针对第 i 个元素在堆中的元素为A[i]的话,那么其左孩子为 A[2i+1] ,右孩子为 A[2i+2] (除了根结点,因为此时根结点下标为 0 )。
一个堆中结点的高度定义为该结点到叶结点最长简单路径上边的个数。
二叉堆可以分成两种形式:最大堆和最小堆。

最大堆
最大堆的性质就是除了根结点以外,其他结点满足A[PARENT(i)]>=A[i],也就是说某个结点的值小于等于其父结点的值。
最小堆
相应的最小堆与最大堆类似,除了根结点以外,其他结点满足 A[PARENT(i)]<=A[i] ,也就是说某个结点的值大于等于其父结点的值。
在堆排序中,我们使用的是最大堆。最小堆通常用在构造优先队列中。

在介绍堆排序算法之前需要了解两个重要的堆的运算,分别是维护堆的性质和建堆。

维护堆的性质(很重要)

维护堆的性质这个操作,核心思想就是,针对一个结点 i ,假设其左右孩子结点为根节点的子树是最大堆,此时A[i]是有可能小于其左右孩子结点的。所以我们只需要分别比较该结点 A[i] 和其左孩子以及右孩子的大小关系,找出最大的值(假设下标为largest)。如果最大值不是在 A[i] 处则交换 A[i] A[largest] 。此时 A[i] 和其左右孩子满足最大堆的性质,但交换后的 A[largest] 继续有可能不满足该性质,因此针对 largest 处的结点递归的调用该方法即可。

伪代码如下:

MAX-HEAPIFY(A,i)
l=LEFT(i)
r=RIGHT(i)
if l<=A.heap-size and A[l]>A[i]
    largest=l
else 
    largest=i
if r<=A.heap-size and A[r]>A[i]
    largest=r
else    
    largest=i

if largest ≠i
    exchange A[i] with A[largest]
    MAX-HEAPIFY(A,largest)

建堆

建堆即是将一个数组 A[1,...,n] 转换最大堆的过程。
我们首先可以知道,对于数组 A[1,...,n] 中的元素 A(n2+1,...,n) 均为叶子结点(只需要考虑第 n 个元素一定为叶结点,则其父结点A[n2]是最后一个有叶结点的结点,那么相应的 A[n2+1] A[n] 均为没有叶子结点的结点,即叶结点)。构建最大堆的过程就是,除了这些叶子结点外,其他所有的结点全都调用一次维护最大堆的性质MAX-HEAPIFY。
伪代码如下:

BUILD-MAX-HEAP(A)
A.heap-size=A.length
for i=(A.length/2)下取整 downto 1
    MAX-HEAPIFY(A,i)

堆排序算法

堆排序算法的思想很简单,首先是调用建堆的过程将一个数组 A[1,...,n] 建成最大堆,此时数组中的最大元素位于最大堆的根结点 A[1] 处,因此交换 A[1] A[n] ,讲最大元素放在 A[n] 的位置。这时, A[n] 已经处于正确的位置上,所以将这个结点 n 从最大堆中去掉(这一操作通过减少A.heap-size的值来实现),剩余的结点中,除了A[1]外,其左右两个子树依旧满足最大堆的性质,因此我们只需要对 A[1] 来调用最大堆的维护过程。
伪代码如下

HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i=A.length downto 2
    exchange A[1] with A[i]
    A.heap-size=A.heap-size-1;
    MAX-HEAPIFY(A,1)

最后根据我们的分析过程给出整个利用最大堆实现堆排序的Java实现代码。

public class HeapSortTest {

    public static void main(String[]args){
        int[] A={ 9, 6, 3, 8, 5, 4, 7, 2, 1};
        HeapSort(A);
        for(int i=0;i<A.length;i++){
            System.out.println(A[i]);
        }
    }

    public static void swap(int[] A,int i,int j){
        int temp =0;
        temp=A[i];
        A[i]=A[j];
        A[j]=temp;
    }

    //排序方法
    public static void HeapSort(int[] A){

        if (A == null || A.length <= 1) {  
            return;  
        }  
        BuildMaxHeap(A);

        for(int i=0;i<A.length;i++){
            System.out.print(A[i]+",");
        }
        System.out.println();

        int n=A.length;
        System.out.println("数组长度:"+n);
        for(int i=n-1;i>=1;i--){
            swap(A,0,i);
            MaxHeapify(A, 0,i);
        }

    }

    //讲给的数组构建成一个最大堆
    public static void BuildMaxHeap(int[] A){
        if (A == null || A.length <= 1) {  
            return;  
        }  
        int n=A.length;
        int index=(int)(n-1)/2;
        for(int i=index;i>=0;i--){
            MaxHeapify(A,i,n);
        }
        for(int i=0;i<A.length;i++){
            System.out.print(A[i]+",");
        }
        System.out.println();
    }

    //维护最大堆的性质方法
    public static void MaxHeapify(int[] A, int index,int heapSize){
        int largest=index;
        int left=index*2+1;
        int right=index*2+2;
        if(left<heapSize && A[left]>A[largest]){
            largest=left;
        }
        if(right<heapSize && A[right]>A[largest]){
            largest=right;
        }
        if(largest!=index){
            swap(A,index,largest);
            MaxHeapify(A,largest,heapSize);
        }

    }

}

最后运行了一下程序。
这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值