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

来源:互联网 发布:php文件上传与下载 编辑:程序博客网 时间:2024/06/05 02:33

比较排序算法

我们平时接触到的一些重要的排序算法,插入排序、归并排序、堆排序及快速排序都属于比较排序算法(即都是通过对元素进行比较操作来确定输入数组的次序)。已经证明了任意比较排序算法排序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=lelse     largest=iif r<=A.heap-size and A[r]>A[i]    largest=relse        largest=iif 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.lengthfor 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);        }    }}

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

0 0