堆是一种根节点和孩子结点具有某种关系的二叉树,具体可以分为大顶堆和小顶堆,其中大顶堆中的所有父结点的值比它的孩子结点的值要大,而左右孩子的值不做对比;反之就是小顶堆。

堆排序是根据堆的性质,进行反复的位置调换和堆调整的过程,要进行堆排序,首先就需要创建堆,这里选择创建大顶堆,创建大顶堆涉及到位置调整,假设对堆中位置为 i 的结点进行位置调整,调整算法为下:

max_heapify(A,i)

1、l =left(i);// i结点的左孩子

2、r=right(i);

3、if l<=heap_size(A) and A[l]>A[i]

4、  then largest=l;

5、  else larges=i;

6、if r<=heap_size(A) and A[r]>A[largest]

7、  then largest=r;

8、if largest != i

9、  then exchange(A[i],A[largest]);

10、  max_heapify(A,largest);//对array[largest]结点继续调整

创建堆的过程就是对结点进行持续调整位置的过程,创建堆的算法描述为:

build_max_heap(A)

1、heap_size(A)=length[A]

2、for i=length[A]/2 downto 1

3、  do max_heapify(A,i);//对结点 i 进行位置调整

这里给出一个序列 4 1 3 2 16 9 10 14 8 7,取自《算法导论》中的内容,现给出其建堆的过程图:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

注意,以上的调整过程,是从左到右、从上到下进行调整的过程。堆建好了,怎么进行排序呢?答案还是进行数的位置调整,我们可以将堆顶的数据和最后一个叶子结点进行位置调换,然后对子序列Array[0,length-2]进行堆调整;继续将堆顶的数据和倒数第二个结点进行位置调换,然后对子序列Array[0,length-3]进行堆调整....,算法描述如下:

heap_sort(A)

1、build_max_heap(A)

2、for i=length(A) down to 2

3、  do exchange( A[1], A[i])//堆顶值和A[i]进行位置调换

4、  heap_size(A)=heap_size(A)-1

5、  max_heapify(A,1)//进行顶点位置的调整

根据算法描述,可以得出程序结果,设计如下:

public class my{
    public static void main(String[] args)throws InterruptedException{
        int arr[]={1,4,23,0,4,5,34,23,4354,23,12};
        printArray(arr);
        heap_sort(arr);
        printArray(arr);
    
    }
    //进行堆排序
    static void heap_sort(int[] array){
        build_max_heap(array);//创建最大堆
        int length=array.length;
        int temp=0;
        for(int i=array.length-1;i>=0;i--){
            temp=array[i];//堆的第一个元素最大,将第一个元素移动到最后,然后对第一个元素进行位置调整
            array[i]=array[0];
            array[0]=temp;
            length--;//由于位置为length及其以后的元素已经排好序了,堆的调整范围是从0到length-1
            max_heapfy(array,0,length);
        }
    }
    //创建最大堆
    static void build_max_heap(int[] array){
        //创建最大堆时,为什么下标从array.length/2开始?因为这样可以将最大的值推到堆顶位置,从以上
        //具体图示位置的调换过程可知,需要从array.length/2开始进行调整。
        for(int i=array.length/2;i>=0;i--){
            max_heapfy(array,i,array.length);
        }
    }
    static void max_heapfy(int[] array,int i,int arraysize){
        int leftchild=i*2+1,rightchild=i*2+2;
        int largest=i,temp=0;
        //以下两个判断是寻找array[i]以及它的左右孩子中值最大的下标
        if(leftchild<arraysize && array[leftchild]>array[i]){
            largest=leftchild;
        }
        if(rightchild<arraysize && array[rightchild]>array[largest]){
            largest=rightchild;
        }
        //最大值的下表如果不是i的话,就进行位置的变动,将array[i]和
        //array[largest]进行位置置换,然后再对位置largest进行max_heapfy调整
        if(largest!=i){
            temp=array[largest];
            array[largest]=array[i];
            array[i]=temp;
            max_heapfy(array,largest,arraysize);
        }
    }    
    static void printArray(int[] array){
        for(int val:array){
            System.out.print(val+" ");
        }
        System.out.println();
    }
}

最后的输出:

1 4 23 0 4 5 34 23 4354 23 12
0 1 4 4 5 12 23 23 23 34 4354

算法分析:

堆排序的时间复杂度是O(nlgn)(最坏的情况下也是如此),空间复杂度是O(1),与归并排序相比,堆排序的空间节省要多很多。

 

posted on 2017-08-23 20:03  Judy518  阅读(133)  评论(0编辑  收藏  举报