數據結構與算法——堆排序

原文連接:https://jiang-hao.com/articles/2020/algorithms-algorithms-heap-sort.htmlhtml

算法介紹

堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。堆排序能夠說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:java

  1. 大頂堆:每一個節點的值都大於或等於其子節點的值,在堆排序算法中用於升序排列;
  2. 小頂堆:每一個節點的值都小於或等於其子節點的值,在堆排序算法中用於降序排列;

堆排序的平均時間複雜度爲 Ο(nlogn)。算法

一般堆是經過一維數組來實現的。在數組起始位置爲0的情形中:api

在堆的數據結構中,堆中的最大值老是位於根節點(在優先隊列中使用堆的話堆中的最小值位於根節點)。堆中定義如下幾種操做:數組

  • 最大堆調整(Max Heapify):將堆的末端子節點做調整,使得子節點永遠小於父節點
  • 建立最大堆(Build Max Heap):將堆中的全部數據從新排序
  • 堆排序(HeapSort):移除位在第一個數據的根節點,並作最大堆調整的遞歸運算

算法步驟

首先第一步和第二步,建立堆,這裏咱們用最大堆;建立過程當中,保證調整堆的特性。數據結構

從最後一個分支的節點開始從右往左,從下至上進行調整爲最大堆。svg

img

如今獲得的最大堆的存儲結構以下:測試

img

初始堆建立完成。ui

接着,最後一步,堆排序,進行(n-1)次循環。this

img

持續整個過程直至最後一個元素爲止。

這個迭代持續直至最後一個元素即完成堆排序步驟。

算法實現

public class HeapSort {
    private int[] arr;
    public HeapSort(int[] arr) {
        this.arr = arr;
    }

    /**
     * 堆排序的主要入口方法,共兩步。
     */
    public void sort() {
        /*
         *  第一步:將數組堆化
         *  beginIndex = 第一個非葉子節點。
         *  從第一個非葉子節點開始便可。無需從最後一個葉子節點開始。
         *  葉子節點能夠看做已符合堆要求的節點,根節點就是它本身且本身如下值爲最大。
         */
        int len = arr.length - 1;
        int beginIndex = (arr.length >> 1)- 1;
        for (int i = beginIndex; i >= 0; i--)
            maxHeapify(i, len);
        /*
         * 第二步:對堆化數據排序
         * 每次都是移出最頂層的根節點A[0],與最尾部節點位置調換,同時遍歷長度 - 1。
         * 而後重新整理被換到根節點的末尾元素,使其符合堆的特性。
         * 直至未排序的堆長度爲 0。
         */
        for (int i = len; i > 0; i--) {
            swap(0, i);
            maxHeapify(0, i - 1);
        }
    }

    private void swap(int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    /**
     * 調整索引爲 index 處的數據,使其符合堆的特性。
     *
     * @param index 須要堆化處理的數據的索引
     * @param len 未排序的堆(數組)的長度
     */
    private void maxHeapify(int index, int len) {
        int li = (index << 1) + 1; // 左子節點索引
        int ri = li + 1;           // 右子節點索引
        int cMax = li;             // 子節點值最大索引,默認左子節點。
        if (li > len) return;      // 左子節點索引超出計算範圍,直接返回。
        if (ri <= len && arr[ri] > arr[li]) // 先判斷左右子節點,哪一個較大。
            cMax = ri;
        if (arr[cMax] > arr[index]) {
            swap(cMax, index);      // 若是父節點被子節點調換,
            maxHeapify(cMax, len);  // 則須要繼續判斷換下後的父節點是否符合堆的特性。
        }
    }

    /**
     * 測試用例
     *
     * 輸出:
     * [0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9]
     */
    public static void main(String[] args) {
        int[] arr = new int[] {3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6};
        new HeapSort(arr).sort();
        System.out.println(Arrays.toString(arr));
    }
}
相關文章
相關標籤/搜索