排序算法(一):堆排序

前言

(二叉堆)是一種用於實現優先隊列模型的數據結構,堆具備堆序(heap order)性每一個節點的關鍵字都大於他的父節點的只有根除外(沒有父親),也能夠是都小於,子節點與父節點的關係決定了這個堆是最小堆仍是最大堆,分別能夠用來作升序和降序。使用優先隊列理論上能夠實現花費O(N log N)時間的排序。數組

方法

對一個數組進行升序的排列,能夠先將數組中的N個元素創建一個二叉堆,這個操做花費O(N)時間,而後對該堆執行N此DeleteMin操做。堆中的數據按照從小到大依次離開堆,將這些元素記錄到第二個數組中,最後再拷貝回來,以此獲得了針對N個元素的排序。每一個DeleteMin操做花費了O(log N)的時間,所以總開銷是O(N log N)
注意到咱們這裏使用了一個額外的數組,空間開銷加大了(將第二個數組數據拷貝回來只花費O(N),不會顯著增長時間消耗)。
雖然咱們在程序中經常爲了節省時間開銷而消耗額外的空間,但在這個問題中有個方法能夠避免。在每次DeleteMin操做以後,堆縮小了1,,所以堆中最後的單元能夠用來存放剛剛刪去的元素。數據結構

DeleteMin(刪除最小元)

這是在排序中重點須要的基本堆操做,要找到須要刪除的元素很容易,難的是刪除,當刪除一個最小元時,在根節點產生了一個空穴,爲了保證堆的性質,必需將堆中最後一個元素X移動,若是它能夠移動到空穴,那麼DeleteMin完成,不過這通常不太可能,所以咱們將空穴的兩個子節點中較小的一個移入空穴,至關於把空穴往下移動了一層,不斷重複這個過程直到X能夠被放到空穴。這種策略被稱做下濾
最小堆與最大堆具備對稱的性質,對於父節點大於子節點的最大堆,區別只是每次刪除的是最大元,上移的是空穴子節點中較大的,與最小堆相反。spa

圖片演示

Heapsort-example.gif

代碼

#define LeftChild(i) (2 * (i) + 1)

void PercDown(ElementType A[], int i, int N) {
    int Child;
    ElementType Tmp;
    
    for (Tmp = A[i]; LeftChild(i) < N; i = Child) {
        Child = LeftChild(i);
        if (Child != N - 1 && A[Child + 1] > A[Child])
            Child++;
        if (Tmp < A[Child])
            A[i] = A[Child];
        else
            break;
    }
    A[i] = Tmp;
}

void Heapsort(ElementType A[], int N) {
    int i;
    
    for (i = N / 2; i >= 0; i--)
        PercDown(A, i, N);
    for (i = N - 1; i > 0; i--) {
        Swap(&A[0], &A[i]);
        PercDown(A, 0, i);
    }
}
相關文章
相關標籤/搜索