數據結構之堆排序(六)

摘要:轉自 http://www.cnblogs.com/MOBIN/p/5374217.html

作爲選擇排序的改進版,堆排序可以把每一趟元素的比較結果保存下來,以便我們在選擇最小/大元素時對已經比較過的元素做出相應的調整。 堆排序是一種樹形選擇排序,在排序過程中可以把元素看成是一顆完全二叉樹,每個節點都大(小)於它的兩個子節點,當每個節點都大於等於它的兩個子節點時,就稱爲大頂堆,也叫堆有序; 當每個節點都小於等於它的兩個子節點時,就稱爲小頂堆。
                             
             (大頂堆(有序堆))                                                                                     (小頂堆)
 
 
算法思想(以大頂堆爲例):
1.將長度爲n的待排序的數組進行堆有序化構造成一個大頂堆
 
2.將根節點與尾節點交換並輸出此時的尾節點
 
3.將剩餘的n -1個節點重新進行堆有序化
 
4.重複步驟2,步驟3直至構造成一個有序序列
 
假設待排序數組爲[20,50,10,30,70,20,80]
 
構造堆
在構造有序堆時,我們開始只需要掃描一半的元素(n/2-1 ~ 0)即可,爲什麼?
因爲(n/2-1)~0的節點纔有子節點,如圖1,n=8,(n/2-1) = 3  即3 2 1 0這個四個節點纔有子節點
                  (圖1:初始狀態)
 
所以代碼4~6行for循環的作用就是將3 2 1 0這四個節點從下到上,從右到左的與它自己的子節點比較並調整最終形成大頂堆,過程如下:
 
第一次for循環將節點3和它的子節點7 8的元素進行比較,最大者作爲父節點(即元素60作爲父節點)
【紅色表示交換後的狀態】
 
 
第二次for循環將節點2和它的子節點5 6的元素進行比較,最大者爲父節點(元素80作爲父節點)
 
 
第三次for循環將節點1和它的子節點3 4的元素進行比較,最大者爲父節點(元素70作爲父節點)
 

第四次for循環將節點0和它的子節點1 2的元素進行比較,最大者爲父節點(元素80作爲父節點)
(注意這裏,元素20和元素80交換後,20所在的節點還有子節點,所以還要再和它的子節點5 6的元素進行比較,這就是28行代碼 i = j 的原因
 
至此有序堆已經構造好了!如下圖:
 
 
調整堆
下面進行while循環
(1)堆頂元素80和尾40交換後–>調整堆
(2)堆頂元素70和尾30交換後–>調整堆
(3)堆頂元素60尾元素20交換後–>調整堆
(4)其他依次類推,最終已排好序的元素如下:

 

 

代碼實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class HeapSort {
    private static void heapSort(int[] arr) {
        int len = arr.length -1;
        for(int i = len/2 - 1; i >=0; i --){ //堆構造
            heapAdjust(arr,i,len);
        }
        while (len >=0){
            swap(arr,0,len--);    //將堆頂元素與尾節點交換後,長度減1,尾元素最大
            heapAdjust(arr,0,len);    //再次對堆進行調整
        }
    }
 
public static  void heapAdjust(int[] arr,int i,int len){
    int left,right,j ;
    while((left = 2*i+1) <= len){    //判斷當前父節點有無左節點(即有無孩子節點,left爲左節點)
        right = left + 1//右節點
        j = left;   //j"指針指向左節點"
        if(j < len && arr[left] < arr[right])    //右節點大於左節點
            j ++;     //當前把"指針"指向右節點
        if(arr[i] < arr[j])    //將父節點與孩子節點交換(如果上面if爲真,則arr[j]爲右節點,如果爲假arr[j]則爲左節點)
            swap(arr,i,j);
        else         //說明比孩子節點都大,直接跳出循環語句
            break;
        i = j;
    }
}
    public static  void swap(int[] arr,int i,int len){
             int temp = arr[i];
              arr[i] = arr[len];
             arr[len] = temp;
    }
    public static void main(String[] args) {
        int array[] = {20,50,20,40,70,10,80,30,60};
        System.out.println("排序之前:");
        for(int element : array){
            System.out.print(element+" ");
        }
        heapSort(array);
        System.out.println("\n排序之後:");
        for(int element : array){
            System.out.print(element+" ");
        }
    }
}

輸出:

排序之前:
20 50 20 40 70 10 80 30 60 
排序之後:
10 20 20 30 40 50 60 70 80 

 

堆排序主要在於理解堆的構造過程和在輸出最大元素後如何對堆進行重新調整,藉助IDE工具的調試功能可以很好的幫助理解整個排序過程。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章