夯實基礎:排序演算法之堆排序

菜鳥︷先飛發表於2020-09-23

概念

堆排序(英語:Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。--《百度百科》

 

堆是什麼?

堆是一個可以被看做一棵樹的陣列物件。

堆有兩種:

每個節點的值都大於或等於其子節點的值,的大頂堆。大頂堆適用於從小到大的排序。

每個節點的值都小於或等於其子節點的值,的小頂堆。小頂堆適用於從大到小的排序。

 

模擬堆排序過程

任務:使用堆排序將陣列從大到小排列。

原始陣列:

以堆的圖形結構顯示:

 

我們從後向前,先把小與父節點的最小節點與父節點交換過程如下:

1.先從節點10開始,因為他的子節點是陣列的最後值。

8沒有兄弟節點,且8 < 10,所以8與10交換。

 

2.我們再來到節點12,節點12有兩個子節點3與5,先比較兩個子節點的大小:3 < 5,所以3再與12比較,3 < 12,所以3與12交換。

 

3.來到節點4,節點4有兩個子節點6與15,先比較兩個子節點的大小:6 < 15,所以6再與4比較:4 < 6,所以節點4不需要交換。

4.來到節點11,節點11有兩個節點3與8,先比較兩個子節點的大小:3 < 8,所以3再與11比較:3 < 11,所以3與11交換。

 

然後交換後的節點11還有兩個子節點5與12,先比較兩個子節點的大小:5 < 12,所以5再與11比較:5 < 11,所以5與11交換。

5.來到節點17,節點17有兩個節點3與4,先比較兩個子節點的大小:3 < 4,所以3再與17比較:3 < 17,所以3與17交換。

 

交換後的節點17有兩個子節點5與8,先比較兩個子節點的大小:5 < 8,所以5再與17比較:5 < 17,所以5與17交換。

 

再次交換後的節點17有兩個節點11與12,先比較兩個子節點的大小:11 < 12,所以11再與17比較:11 < 17,所以11與17交換。

 

這樣,我們就完成了一個小頂堆。

獲取到了陣列最小值3,然後3與10交換,刨除3再次完成小頂堆。

 

重複上面的步驟直到小頂堆長度為1。

 

堆排序程式碼

public static void minHeapDown(int[] array, int start, int end) {
    int current = start;            // 當前(current)節點的位置
    int left = 2 * current + 1;        // 左(left)孩子的位置
    int tmp = array[current];            // 當前(current)節點的大小

    for (; left <= end; current = left, left = 2 * left + 1) {
        // "left"是左孩子,"left+1"是右孩子
        if (left < end && array[left] > array[left + 1])
            left++;        // 左右兩孩子中選擇較小者
        if (tmp <= array[left])
            break;        // 調整結束
        else {            // 交換值
            array[current] = array[left];
            array[left] = tmp;
        }
    }
}

public static void HeapSort(int[] array) {
    int start, tmp;
    int length = array.length;

    for (start = length / 2 - 1; start >= 0; start--)
        minHeapDown(array, start, length - 1);
    // 從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
    for (start = length - 1; start > 0; start--) {
        // 交換array[0]和array[i]。交換後,array[i]是array[0...i]中最小的。
        tmp = array[0];
        array[0] = array[start];
        array[start] = tmp;
        // 調整array[0...i-1],使得array[0...i-1]仍然是一個最小堆。
        // 即,保證array[i-1]是array[0...i-1]中的最小值。
        minHeapDown(array, 0, start - 1);
    }
}

 

堆排序效率

時間複雜度:堆排序的時間複雜度跟歸併排序是一樣的,都是N個元素迴圈logN次,所以時間複雜度為O(N * logN)。

空間複雜度:O(1)。

穩定性:不穩定。因為堆排序是根據父節點選擇比較進而交換排序,與父節點大小關係的不確定性,進而出現A1大於父節點不發生交換,A2小於父節點發生交換操作,造成A2排到了A1前面。所以堆排序是不穩定的。

相關文章