演算法導論-堆排序

QingLiXueShi發表於2015-03-11

堆排序的時間複雜度是,具有空間原址性,即任何時候都只需要常數個額外的元素空間儲存臨時資料。

 

一、堆

二叉堆是一個陣列,可看成一個近似的完全二叉樹,樹上的每個結點對應陣列中的一個元素。除了最底層外,該樹是完全充滿的,而且是從左到右填充。

二叉堆可以分為兩種形式:最大堆和最小堆。在最大堆中除根節點外所有結點i都要滿足:,即某個結點的值至多與其父結點一樣大。在最小堆中除根節點外所有結點i都要滿足:

說明:堆排序中,我們使用最大堆,最小堆通常用於構造優先佇列。

 

二、維護堆的性質

函式MAX-HEAPIFY的輸入為一個陣列A和下標i,假定根節點為LEFT(i)和RIGHT(i)的二叉樹都是最大堆,通過讓A[i]的值在最大堆中逐級下降,從而使得以下標i為根結點的子樹為最大堆。

函式MAX-HEAPIFY的時間代價包括:調整A[i]、A[LEFT[i]]和A[RIGHT[i]]關係的時間代價,加上以一顆i的一個孩子為根結點的子樹上執行MAX-HEAPIFY的時間代價(假設遞迴呼叫會發生)。

 

下面首先證明每個子樹的大小至多為2n/3。

證明:設堆的高度為h,最後一層結點個數為m,則整個堆的結點總數為:

根結點的左子樹結點總數為:

根結點的右子樹結點總數為:,其中

當最底層恰好半滿的時候,,則

解出:

 

因此,每個子樹的大小至多為2n/3(最壞情況發生在樹的最底層恰好半滿的時候),MAX-HEAPIFY的執行時間為:

,解出

 

三、建堆

可以利用自底向上的方法利用MAX-HEAPIFY把一個大小為n的陣列轉換為最大堆,子陣列A[n/2+1…n]中的元素都是葉子結點,每個葉子結點可看成只包含一個元素的堆。

可以簡單估算函式BUILD-MAX-HEAP執行時間的上界。每次呼叫MAX-HEAPIFY的時間複雜度是,BUILD-MAX-HEAP需要次這樣的呼叫,因此總的時間複雜度是

說明:

(1)這個上界雖然正確,但不是漸進準確的。因為不同結點執行MAX-HEAPIFY的時間與該結點的高度有關,且大部分結點高度都很小可以證明能夠線上性時間內,把一個無序陣列構造成為一個最大堆。

(2)也可以通過呼叫BUILD-MIN-HEAP線上性時間內,把一個無序陣列構造成為一個最小堆。BUILD-MIN-HEAP與BUILD-MAX-HEAP完全相同。

(3)因為有n個結點,最後一個元素序號為n,那麼它的parent結點應該是序號最大的parent結點,那麼這個parent結點就為[n/2],其之後都是葉子結點,為[n/2] + 1, [n/2] + 2, ..., n。

 

四、堆排序演算法

思路:初始時,利用BUILD-MAX-HEAP將陣列A[1…n]建成最大堆。因為陣列中最大元素總在根結點A[1]中,通過它與A[n]進行互換,可讓最大元素放到正確的位置。然後,從堆中去掉結點n,剩餘結點中,原來根的孩子結點仍然是最大堆,新的根結點可能會違背最大堆的性質。為了維護最大堆的性質,呼叫MAX-HEAPIFY(A,1),從而在A[1…n-1]上構造一個新的最大堆。堆排序演算法會不斷重複這個過程,直到堆的大小由n-1降為2。

HEAPSORT過程的時間複雜度是,因為每次呼叫BUILD-MAX-HEAP的時間複雜度是,而n-1次呼叫

MAX-HEAPIFY,每次的時間為

下面給出堆排序演算法的參考程式:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 #define LEFT(i)        2 * i
 5 #define RIGHT(i)    2 * i + 1
 6 
 7 class MaxHeap
 8 {
 9 public:
10     void BuildMaxHeap(int *A, int len);
11     void MaxHeapfy(int *A, int i, int len);
12     void HeapSort(int *A, int len);
13 };
14 
15 void MaxHeap::MaxHeapfy(int *A, int i, int len)
16 {
17     int left = LEFT(i);
18     int right = RIGHT(i);
19     int large;
20 
21     if (left <= len && A[left - 1] > A[i - 1])
22     {
23         large = left;
24     }
25     else
26     {
27         large = i;
28     }
29 
30     if (right <= len && A[right - 1] > A[large - 1])
31     {
32         large = right;
33     }
34 
35     if (i != large)
36     {
37         int tmp = A[i - 1];
38         A[i - 1] = A[large - 1];
39         A[large - 1] = tmp;
40 
41         MaxHeapfy(A, large, len);
42     }
43 }
44 
45 void MaxHeap::BuildMaxHeap(int *A, int len)
46 {
47     for (int i = len / 2; i > 0; --i)
48     {
49         MaxHeapfy(A, i, len);
50     }
51 }
52 
53 void MaxHeap::HeapSort(int *A, int len)
54 {
55     BuildMaxHeap(A, len);
56 
57     for (int i = len; i > 1; --i)
58     {
59         int tmp = A[0];
60         A[0] = A[i - 1];
61         A[i - 1] = tmp;
62 
63         MaxHeapfy(A, 1, i - 1);
64     }
65 }
66 
67 //Test
68 int main()
69 {
70     MaxHeap Test;
71     int A[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
72 
73     Test.HeapSort(A, 10);
74     for (int i = 0; i < 10; ++i)
75     {
76         cout << A[i] << " ";
77     }
78     cout << endl;
79 
80     return 0;
81 }

相關文章