#排序演算法#【3】堆排序

範長法@三月軟體發表於2014-05-31

  堆是一個完全二叉樹,樹中每個結點對應於原始資料的一個記錄,並且每個結點應滿足以下條件:非葉結點的資料大於或等於其左、右孩子結點的資料(若是按從大到小的順序排序,則要求非葉結點的資料小於或等於其左、右孩子結點的資料)。

由堆的定義可看出,其根結點為最大值,堆排序就是利用這一特點進行的。堆排序過程包括兩個階段:

1)將無序的資料構成堆(即用無序資料生成滿足堆定義的完全二叉樹)。

2)利用堆排序(即用上一步生成的堆輸出有序的資料)。

   假如有一組要排序的資料:69,65,90,37,92,6,28,54

  1.首先,根據堆的定義,將這組資料生成堆

    a. 從最後一個非葉節點37開始,比較其子節點,若發現左右節點中有一個比該節點大,則將兩個節點進行交換

    b. 按照上面的步驟,一遍下來可以將樹中最大的節點92篩選到最上面的根節點

    

 

   2.利用堆進行排序

    a. 因為上面排序一遍過後,最大的節點在根節點,因此將根節點與最後一個節點交換,使最大的節點排在最後

    b. 由於根節點和最後一個節點交換,破壞了原來的堆結構,所以需要對除了最後一個節點外的其他n-1個節點重新生成堆

    c. 重複上面兩個步驟,最後剩下的一個節點即為最小節點,排在整個二叉樹的根節點,至此則生成從小到大的有序堆

    具體圖示如下:

      

(圖中輸出,即表示為調整到陣列最後)

 

下面貼上具體程式碼(C語言版):

 1 #include <stdio.h>
 2 #include "CreateData.c"  //生成隨機數檔案
 3 
 4 #define MAXSIZE 10
 5 
 6 //對指定的節點構成堆
 7 //int m    指定的節點
 8 //int n    陣列的元素個數
 9 void HeapAdjust(int a[],int m,int n){
10     int i,j,t;
11     
12     while(2*m+1 < n){    //指定的節點有左子樹
13         j = 2*m+1;    //m的左子樹
14         
15         if(j+1 < n){
16         //有右子樹,且右子樹大於左子樹
17             if(a[j] < a[j+1])    j++;
18         }
19 
20         
21         if(a[m] < a[j]){
22         //如果有個子樹大於根節點,則交換
23             t = a[m];
24             a[m] = a[j];
25             a[j] = t;    
26 
27             m = j;    //交換過以後,以j為根的節點再重新生成堆
28         }else
29             break;
30         
31     }
32 }
33 
34 //堆排序演算法
35 void HeapSort(int a[],int n){
36     int i,t;
37 
38     i = n/2-1;
39 
40     for(;i>=0;i--)
41         HeapAdjust(a,i,n);
42     
43     for(i = n-1;i>=0;i--){
44         //將樹根節點(最大)放到最後一個元素
45         t = a[i];
46         a[i] = a[0];
47         a[0] = t;
48 
49         //破壞了根節點,再對根節點重新生成堆
50         HeapAdjust(a,0,i);
51     }
52 }
53 
54 int main(){
55     int a[MAXSIZE];
56     int i;
57 
58     
59     if(!CreateData(a,100,10,MAXSIZE)){
60         printf("生成隨機數失敗.\n");
61         return 0;
62     }
63 
64     printf("排序前:");
65     for(i = 0 ; i < MAXSIZE ;i++)
66         printf("%d ",a[i]);
67 
68     HeapSort(a,MAXSIZE);
69 
70     printf("\n排序後:");
71     for(i = 0 ; i < MAXSIZE ;i++)
72         printf("%d ",a[i]);
73     printf("\n");
74 
75     return 1;
76 }

  此段程式碼在除錯的過程中比較坎坷,在Linux中有時候會發現比較古怪的問題,比如陷入死迴圈但是很難追蹤,不過都是個人粗心,有幾個變數賦值和迴圈條件剛開始的時候寫錯了。不過現在經測試已經執行穩定,並實現從小到大進行資料排序功能。

 

相關文章