排序之堆排序

来源:互联网 发布:java nio path 编辑:程序博客网 时间:2024/06/10 07:18

                               排序之堆排序

    堆排序是一种树形选择排序方法,它的特点是,在排序过程中,将a[0…n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点与孩子节点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。

    先来介绍一下二叉堆的定义:父节点的值总是大于等于(或小于等于)任何子节点的值;同时每个节点的左子数与右子树也均为二叉堆。前者称为最大堆,后者称为最小堆,我们这里使用最小堆,每次挑选最小的元素归位。下图即为一个最小堆:

 

    具体的实现过程为:把待排序的序列存储在数组a[0…n]中,将a看做一棵二叉树,每个节点表示一个元素,源序列的第一个元素a[0]作为二叉树的根,以下各元素a[1…n]依次逐层从左到右顺序排列,构成一棵完全二叉树,节点a[i]的左孩子为a[2i+1],右孩子为a[2i+2],双亲是a[(i-1)/2]。(这里需要注意,因为我们是从a[0]开始保存序列的,所以左孩子右孩子及双亲的关系是上面给出的形式,如果是从a[1]开始存储,则关系要相应变化)如下图表示堆的逻辑结构与存储结构:


                                                                                                                  1      3      5     6      8

   逻辑结构                                                                                                          存储结构

 

     堆排序的关键是构造初始堆,因为数组具有对应的树的表示形式,但一般情况下树并不满足堆的条件,通过重新排列元素,可以建立一棵“堆化”的树。

    在这里我们先要明确一下思路,首先我们需要把树通过调整变为最小堆,则最小堆的根节点(也就是数组的第一个元素)即满足了是该序列的最小值,然后我们把它和数组的最后一个元素交换(目的就是把当前的最小值保存到数组的尾部),接下来我们对新形成的树(除去最后一个节点,因为最后一个节点已经是当前最小值了)进行调整,再次变为新的最小堆,最小堆的根节点即为当前新的最小值,再把该最小值与数组的倒数第二个数交换,则数组的倒数第二个数子即为序列的第二小的数字;现在,以此类推,直到新的树中只有一个节点为止,则此时,保存在数组中的数字满足从大到小的顺序。这就是堆排序作为选择排序的一种的排序过程。

    难点来了,如何构建最小堆?首先我们假设一棵树除根节点外均已满足二叉堆的要求,现在要做的就是把该根节点调整到合适的位置上即可满足整个树是一个二叉堆,下面给出代码帮助理解:

//从节点i开始调整,n为节点数,从0开始计算,i的子节点为i*2+1,i*2+2//注意区分好下标数与元素个数void MinHeapFixdown(int *a, int i, int n){       intj,temp;       temp= a[i];       j= i*2 + 1;       while(j<n)       {              if(j+1<n&& a[j+1]<a[j])                     j++;//在子节点中选择最小的数              if(a[j]>=temp)                     break;              a[i]=a[j];//把较小的节点往上移,替换他的父节点              i=j;              j=2*i+1;       }       a[i]= temp;//把根节点沉到合适的位置}
     那么一棵直接由数组得到的树如何调整为二叉堆呢?这里我们首先要明白一棵树的叶子节点已经满足了二叉堆的要求,我们需要做的就是从最后一个叶子节点的父节点开始调整,直到调整到根节点为止,此时该树满足二叉堆,代码如下(区分好元素下标与元素个数):

//建立最小堆void MakeMinHeap(int *a, int n)//n为个数{       for(inti=n/2-1; i>=0; i--)//n为下标 i=(n-1-1)/2,叶子节点已经是符合要求的              MinHeapFixdown(a,i,n);}
    下面的就简单了,通过逐次的建立最小堆并交换数组首尾的元素即可得到有序的序列,代码如下:

//用最小堆排序得到的是由大到小的顺序(按数组下标由小到大)//先建立最小堆才可以使用void Sort_heap(int *a, int n){     for(int i=n-1; i>=1;i--)     {            //区分i是下标还是元素个数            swap(a[i],a[0]);            MinHeapFixdown(a,0,i);     }}
    至此,堆排序实现。堆排序的最坏时间复杂度为O(nlog2(n)),堆排序的平均性能分析较难,但研究表明,它接近于最坏性能,另外堆排序属于就地排序。

0 0
原创粉丝点击