「《算法導論》之‘排序’」:堆排序

  關於堆排序的介紹主要引自一博文,比較詳細的例子可參考另外一博文html

  動畫演示能夠參考一網頁git

  關於二叉堆,有一博文二叉堆(一)之 圖文解析寫的很清晰詳細,很值得參考。github

  堆給人的感受是一個二叉樹,可是其本質是一種數組對象,由於對堆進行操做的時候將堆視爲一顆徹底二叉樹,樹中每一個節點與數組中的存放該節點值的那個元素對應。因此堆又稱爲二叉堆,堆與徹底二叉樹的對應關係以下圖所示:算法

  

  一般給定節點i,能夠根據其在數組中的位置求出該節點的父親節點、左右孩子節點,這三個過程通常採用宏或者內聯函數實現。書(《算法導論》)上介紹的時候,數組的下標是從1開始的,因此可獲得:PARENT(i)=i/2  LEFT(i) = 2*i   RIGHT(i) = 2*i+1。數組

  注:我的在程序實現的時候默認是從0開始的,所以left = 2*i+1, right = 2*i+2。函數

  根據節點數值知足的條件,能夠將分爲最大堆和最小堆。最大堆的特性是:除了根節點之外的每一個節點i,有A[PARENT(i)] >= A[i],最小堆的特性是:除了根節點之外的每一個節點i,有A[PARENT(i)] >=A[i]。動畫

  把堆當作一個棵樹,有以下的特性:ui

  1)含有n個元素的堆的高度是lgn。this

  2)當用數組表示存儲了n個元素的堆時,葉子節點的下標是n/2+1,n/2+2,……,n。spa

  3)在最大堆中,最大元素該子樹的根上;在最小堆中,最小元素在該子樹的根上。

保持堆的性質

  堆個關鍵操做過程是如何保持堆的特有性質,給定一個節點i,要保證以i爲根的子樹知足堆性質。書中以最大堆做爲例子進行講解,並給出了遞歸形式的保持最大堆性的操做過程MAX-HEAPIFY(注:個人程序中名爲adjustHeap)。先從看一個例子,操做過程以下圖所示:  

  

  從圖中能夠看出,在節點i=2時,不知足最大堆的要求,須要進行調整,選擇節點2的左右孩子中最大一個進行交換,而後檢查交換後的節點i=4是否知足最大堆的要求,從圖看出不知足,接着進行調整,直到沒有交換爲止。

建堆

  創建最大堆的過程是自底向上地調用最大堆調整程序將一個數組A[1.....N]變成一個最大堆。將數組視爲一顆徹底二叉樹,從其最後一個非葉子節點(n/2)開始調整。調整過程以下圖所示:

  

堆排序算法

  堆排序算法過程爲:先調用建立堆函數將輸入數組A[1...n]形成一個最大堆,使得最大的值存放在數組第一個位置A[1],而後用數組最後一個位置元素與第一個位置進行交換,並將堆的大小減小1,並調用最大堆調整函數從第一個位置調整最大堆。給出堆數組A={4,1,3,16,9,10,14,8,7}進行堆排序簡單的過程以下:

  (1)建立最大堆,數組第一個元素最大,執行後結果下圖:

  (2)進行循環,從length(A)到1,並不斷的調整最大堆,給出一個簡單過程以下:

 

代碼實現

 1 void HeapSort::sort()
 2 {
 3     buildHeap();
 4     for (int i = 0; i < len; i++)
 5     {
 6         int hLen = len - i;
 7         // It is been done in buildHeap() when i =  0
 8         if (i != 0)
 9             adjustHeap(0, hLen);
10         exchange(0, hLen - 1);
11     }
12 }
13 
14 void HeapSort::buildHeap()
15 {
16     int n = 1;
17     while (len > pow(2.0, n) - 1)
18     {
19         adjustHeap(0, len);
20         n++;
21     }
22 }
23 
24 void HeapSort::adjustHeap(int start, int hLen)
25 {
26     if (start >= hLen - 1)    return;
27 
28     int left = 2 * start + 1;
29     int right = 2 * start + 2;
30 
31     // This also means left < hLen and
32     // arr[left] and arr[right] exist.
33     if (right < hLen)
34     {
35         // This means arr[start] is smaller than one of its child, so we just
36         // need to find a larger child to replace it.
37         if (arr[start] < arr[left] || arr[start] < arr[right])
38         {
39             if (arr[left] >= arr[right])
40             {
41                 exchange(start, left);
42             }
43             else
44             {
45                 exchange(start, right);
46             }
47         }
48     }
49     // This means left < hLen <= right and arr[right] does not exist.
50     else if (left < hLen)
51     {
52         if (arr[start] < arr[left])
53         {
54             exchange(start, left);
55         }
56     }
57     // This means hLen <= left, arr[left] does not exist and arr[start] has no child.
58     // Actually, 'else' branch is not necessary as 'if (start >= hLen - 1)    return;'
59     // at the beginning has ensured this.
60     else
61     {
62         return;
63     }
64 
65     adjustHeap(start + 1, hLen);
66     adjustHeap(start + 2, hLen);
67 }

 

  完整程序請見Github.

相關文章
相關標籤/搜索