堆排序

  堆排序是另外一種基於選擇的排序方法。它是一種樹形選擇排序,利用了堆頂記錄的關鍵字最大(或最小)這一特徵,使得當前無序去中選取最大或(最小)關鍵字變得簡單。html

  堆的定義:有n個元素組成的序列{k0,  k1, k2, k3, ……, kn-2, kn-1 },當且僅當知足關係: ki <= k2i+1 且 ki <= k2i+2 (或者 ki >= k2i+1 且 ki >= k2i+2)其中i = 0,1,2,……,[(n/2]-1時,稱之爲堆。 例如序列{47, 35, 27, 26, 18, 7, 13, 19} 就知足上面的條件,這個序列就是一個堆。算法

  若將堆當作是一棵以k0爲根的徹底二叉樹,則,這棵徹底二叉樹的每一個非終結點的值均不大於(或小於)其左、右孩子結點的值。由此能夠看出,若一個徹底二叉樹是堆,則根節點必定是這n個結點中的最小值或最大值。數據結構

  堆排序的基本思想是:首先將待排序的記錄序列構造一個堆。此時選出了堆中全部記錄的最大者(或最小者),而後將它從堆中移走,將剩餘的記錄再調整成堆,有找出次大者,以此類推,直到堆中只剩下一個元素,出堆的記錄順序就是一個有序序列。ide

  堆排序涉及到建堆和出堆兩個過程,咱們能夠把每一個過程都看做是對堆的調整。咱們假定當前要調整的結點的下角標爲K(從零開始算),堆最後的一個結點下角標m,那咱們的調整過程是指使得以結點K爲根節點的子樹知足堆的條件。若是每一個結點都知足堆的條件,那麼整個序列天然就是一個堆。所以咱們能夠把調整描述爲:spa

  (1) 設置兩個指針 i 和 j, i 指向當前的結點 i = K, j 指向當前節點的左孩子 j = 2* i + 1;3d

  (2)選取當前結點的左右孩子的值,選取較大值(或較小值),並用 j 指向該孩子的結點;指針

      if( j <= m && j+1 <= m && a[j] < a[j+1] )  j++;code

  (3)用當前結點的值比較j所指向的結點的值,根據比較的結果,結束調整(知足堆的條件)或交換結點內容(調整當前結點的值)並繼續調整(子結點)htm

  

  建堆的過程能夠這樣理解,選取序列的中間節點(假定結點個數爲n,那麼中間節點的索引爲[n/2]-1 (從零開始)),那麼這個節點以後的結點在堆中必定是做爲他們的後續子節點的索引號超過 n。若是調整中間結點前的全部結點,那麼整個序列將變成一個堆。blog

 

  參考代碼:

  

#include <stdio.h>

#define MAX_NUM 80

void sift(int* a, int k, int m)
{
    int i = k;
    int j = 2*i+1;
    
    
    while(j <=m)
    {
        int temp = a[i];
        if(j <= m && j+1 <= m && a[j] < a[j+1]) j++;
        if(a[i] > a[j]) break;
        else
        {
            a[i] = a[j];
            a[j] = temp;
            i = j;
            j = 2*i + 1;            
        }    
    }
    for(int i = 0; i <=m ;i++)
    {
        printf("%d ",a[i]);
    } 
    printf("\n");
    
}

void heapsort(int* a, int n)
{
    int h = n/2;
    
    printf("建堆過程:\n");
    for(int i = h-1; i >=0; i--)  //建初始堆,從最後一個非終結點至根節點 
        sift(a,i,n-1);
    
    printf("\n");
    printf("堆調整過程\n"); 
    for(int i = n-1; i > 0;i--)  // 重複執行移走堆頂節點及從新構建堆的操做 
    {
        int temp = a[0];
        a[0] = a[i];
        a[i] = temp;
        sift(a,0,i-1);
    }
    
    printf("最後排序的結果:\n");
    
    for(int i = 0; i < n ;i++)
    {
        printf("%d ",a[i]);
    } 
    printf("\n");
    
}

int main(int argc,char* argv[])
{
    int a[MAX_NUM];
    int n;    

    printf("Input total numbers: ");
    scanf("%d",&n);

    if( n > MAX_NUM ) n = MAX_NUM;
    
    for(int i = 0; i < n;i++)
    {
        scanf("%d",&a[i]);
    } 
        
    heapsort(a,n);
    
    
    
    
    return 0;
}
View Code

  如下列序列做爲案例: 125 11 22 34 15 44 76 66 100 8 14 20 2 5 1       共15個元素

  案例運行截圖:

    

 

  

  

   堆排序算法效率與穩定性分析
     對於一個深度爲K的堆,「篩選」所須要進行的比較次數之多爲2(k-1);

     對於n個元素,簡稱深度爲 h = ([logn]+1)的堆,所須要進行的比較次數之多爲4n; 

            調整「堆頂」n-1次,總共進行的關鍵字比較的次數不超過   2[log(n-1)]+ [log(n-2)]+ …… + [log(2)] < 2n[log(n)]

            所以堆排序的在最壞的狀況下,時間複雜度爲O(nlogn),這是堆的最大優勢。堆排序方法在記錄較少的狀況下並不提倡,可是對於記錄較多的數據列表仍是頗有效的,由於運行時間主要耗費在建初始堆和調整新建堆時運行的反覆調整中。 在堆排序算法中只須要一個暫存調整記錄內容的單元和兩個變量,因此堆排序是一種速度快且省空間的排序算法。在堆的重建過程會根據堆的需求改變堆的位置,故堆排序是一種不穩定的排序算法。

 

    注:主要參考彭軍、向毅主編的 《數據結構與算法》

相關文章
相關標籤/搜索