算法之“堆排序”

在了解之前先看一下如何判断一个序列为堆序列

一、对堆的理解

堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序;它的最好、最坏、平均时间复杂度均为O(nlogn),它是不稳定排序。

参考:如何判断一个序列为堆(堆与完全二叉树的关系)

有的文章称大根堆与小根堆,我这里成为最大堆和最小堆。

二、堆排序

例如一个数组中的数据为{2,5,7,3,9}

                                   

对应的完全二叉树为:

                                   

堆排序过程:

    1、初始化堆(将数组对应的二叉树排列成最大堆或最小堆的形式,这里我采用最大堆)

                                   

注意:每次交换后都要对下层的子堆进行递归调整,因为交换后有可能破坏已调整子堆的结构。

    2、调整堆,采用循环将数组重新排序,每次将根节点的值与堆尾部元素交换,count值每次减一,获得新的无序数组arr[count],被交换的元素再次与它的下一层左右孩子进行比较、调整

                                             

             对应数组元素为:

                                   

  3、重复第二步,交换元素之前先调整堆结构

                                 

最后数组array={12,9,7,5,3,2};

代码实现:

public static void main(String[] args){
    int array={2,5,7,3,9,12};

    System.out.println("排序之前,array=");
    showArray(array);


    initHeap(array);
    System.out.println("建堆之后,array=");
    showArray(array);

    sortHeap(array);
    System.out.println("堆排序之后,array=");
    showArray(array);

}


//做数组的轮询
private void showArray(int[] array){
    for(int i=0;i<array.length;i++){
        System.out.print(array[i]);
    }
    System.out.println();
}


//将组数转换为二叉树形式,同时变成完全二叉树(最大堆或最小堆)
private void initHeap(int[] array){
    //开始建堆,从最后一个非叶子节点开始
    //注意:最后一个叶子节点的下标(索引)为array.length/2-1;
    int root=array.length/2-1;
    for(root;root>=0;root--){
         adjustHeap(array,array.length,root);   
    }
}


//调整数组,使之成为完全二叉树(最大堆/最小堆)
public static void adjustHeap(int[] array,int length,int root){
     int maxChildIndex;
    //待调整子堆的根节点必须是非叶子节点
    while(root<count/2-1){
    //需要在根root、leftchild、rightchild中找到最大值,然后交换位置
    if(root==count/2-1 && count%2==0){//判断节点的个数,如果为偶数,它最后一个非叶子节点没有右子树
        maxChildIndex=2*root+1;
     }else{
            int leftChildIndex=2*root+1;
            int rightChildIndex=2*root+2;
            if(array[leftChildIndex] < array[rightChildIndex]){
                 maxChildIndex=rightChildIndex;
             }else{
                 maxChildIndex=leftChildIndex;
             }

      }
        //子节点与根节点进行值比较
        if(array[root] < array[maxChildIndex]){
            int tem=array[root];
            array[root]=array[maxChildIndex];
            arrau[maxChildIndex]=tem;
        //注意:要继续调整因交换结构而改变的子堆****重新从非叶子节点开始做数组的轮询*******
        root=maxChildIndex;
        
        }else{
            return;//此刻表示堆结构初始化完成
        }
}
    
        
public static void sortHeap(int[] array){
    initHeap(array);//做一次堆初始化,使之成为完全二叉树
    
    int count=array.length;
    while(count>1){
        int tem=array[count-1];
        array[count-1]=array[0];
        array[0]=tem;
        count--;//将数组中的第一个数,是本次堆初始化后最大的一个数,取出交换位置,count--,未排序的数字又少了一个
        adjustHeap(array,count,0);//参数root=0,调整过程自上而下
    }

输出结果:

排序之前,array=

2 5 7 3 9 12

建堆之后,array=

12 9 7 3 5 2

排序之后,array=

12 9 7 5 3 2

多练习! 多练习! 多练习!重要的事情说三遍!

猜你喜欢

转载自blog.csdn.net/weixin_38664232/article/details/85128065