排序算法总结之堆排序

一,堆排序介绍

堆是一个优先级队列,对于大顶堆而言,堆顶元素的权值最大。将 待排序的数组 建堆,然后不断地删除堆顶元素,就实现了排序。关于堆,参考:数据结构--堆的实现之深入分析 

下面的堆排序算法将数组中的元素从小到大排序,用大顶堆来实现。

二,堆排序算法分析

现给定了一维数组,需要将数组中的元素使用堆排序。首先,得创建一个堆,可以在这个给定的一维数组上建堆。 对N个元素 建堆的时间复杂度为O(N)

堆排序具体的细节实现有两种方式:一种方式是将堆顶元素删除后,放到一个辅助数组中,然后进行堆调整使之成为一个新。继续删除堆顶元素,直至将堆中所有的元素都出堆,此时排序完成。这种方式需要一个额外的辅助空间O(N)

另一种方式是:将每次删除的堆顶元素放到数组的末尾。因为,对于堆的基本操作 delMin/delMax 而言(delMin针对的是小顶堆,delMax针对的是大顶堆,原理一样)是将堆中的最后一个元素替换堆顶元素,然后向下进行堆调整。因此,可以利用这个特点将每次删除的堆顶元素保存在数组末尾,当所有的元素都出堆后,数组就排好序了。这种方式不需要额外的辅助空间,空间复杂度为O(1)

三,堆排序算法实现

public class HeapSort {
   
    public static <T extends Comparable<? super T>> void heapSort(T[] arr){
        //build heap
        for(int i = arr.length/2 - 1; i >= 0; i--)
            percDown(arr, i, arr.length);
       
       
        for(int i = arr.length - 1; i >= 0; i--)
        {
            swapReference(arr, 0, i);//delete Max
           
            percDown(arr, 0, i);// 从根开始向下堆调整
        }
    }
   
    private static <T extends Comparable<? super T>> void swapReference(T[] arr, int from, int to){
        T tmp;
        tmp = arr[from];
        arr[from] = arr[to];
        arr[to] = tmp;
    }
   
    //求解 i 的左孩子
    private static int leftChild(int i){
        return 2*i + 1;
    }
   
    /**
    *
    * @param arr 存储堆的一维数组
    * @param i 从 i 位置开始进行向下堆调整
    * @param n 堆中元素的个数(不是数组的长度)
    */
    private static <T extends Comparable<? super T>> void percDown(T[] arr, int i, int n){
        int child;
        T tmp;//保存当前待调整的结点,当找到了合适的位置后,需要将之放入到合适位置,以保持堆序性质
       
        for(tmp = arr[i];  leftChild(i) < n; i = child)
        {
            child = leftChild(i);
            if(child != n-1 && arr[child].compareTo(arr[child+1]) < 0)
                child++;//右孩子更大
            if(tmp.compareTo(arr[child]) < 0)
                arr[i] = arr[child];//父节点下移
            else
                break;//父节点比左右孩子都大时,不需要再向下移动了
        }
        arr[i] = tmp;//将节点放入合适的位置
    }
   
    //for test purpose
    public static void main(String[] args) {
        Integer[] arr = {31,41,59,26,53,58,97};
        heapSort(arr);
        for (Integer i : arr) {
            System.out.print(i + " ");
        }
    }
}

有几个细节地方解释一下:

①在第3行的heapSort方法中,第5-6行是建堆操作,因为数组中的元素是从下标0开始存储的,故最后一个非叶子结点的下标为:arr.length/2 - 1

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/0fd3c40a89aa42bb60df8806ebbe9643.html