歡迎指出其中的錯誤和交流。
關於堆排序的具體介紹和C代碼實現見該鏈接。
算導關於堆排序的練習主要是一些證明,可以幫助理解堆的特征。部分練習是圖示過程,這些練習認真用筆過一次會很有收獲。
1.在高度為h的堆中,最多和最少的元素個數是多少?
最多:底層全滿;1 + 2^1 + 2^2 + ...... + 2^h,等比數列求和得2^(h+1) - 1
最少:底層只有一個節點;1 + 2^1 + 2^2 + ...... + 2^(h-1) + 1,等比數列求和得2^h - 1 + 1 = 2^h
2.證明:含n個元素的堆的高度為floor(lgn)
假設n個元素的堆的高度為h。由上題得2^h <= n <= 2^(h+1) - 1,因此h <= lgn < h+1。
根據floor 和 ceiling 函數的性質
見維基
得h = floor(lgn)
3.證明:在一個最大堆的某棵子樹中,最大元素在該子樹的根上。(看了答案才知道用反證法,居然想不到反證法,離散數學白看了。。。。。)
假設這個命題為錯誤的,存在一棵子樹的最大元素不在該子樹的根,最大元素的下標為m。
則下標為m的節點的值比其父節點的值大,但是最大堆的特性為某個節點的值最多和其父節點的值一樣大,矛盾,因此假設為錯誤,命題正確。
4.證明:當用數組表示存儲了m個元素的堆時,葉節點的下標是floor(n/2)+1, floor(n/2)+2,...n
剛開始打算通過公式證明最有一個葉節點的父節點為floor(n/2),最后沒能成功(數學太渣)。。。
根據二叉堆的性質:某節點下標為i(非根節點),其父節點的下標為floor(i/2),因此最后一個葉節點的父節點的下標為floor(n/2),所以從下標floor(n/2)+1開始到n都是葉節點。
5.寫出最小堆中維持最小堆性質的操作(本來要求寫偽代碼)

1 void min_heapify(int A[], int length, int i){ 2 int l = 2 * i; 3 int r = 2 * i + 1; 4 int smallest; 5 6 if(l <= length && A[l] < A[i]) 7 smallest = l; 8 else 9 smallest = i; 10 if(r <= length && A[r] < A[smallest]) 11 smallest = r; 12 13 if(smallest != i){ 14 int temp = A[smallest]; 15 A[smallest] = A[i]; 16 A[i] = temp; 17 min_heapify(A, length, smallest); 18 } 19 }
6.將max_heapify()函數的遞歸調用改為迭代結構,使效率提高。

1 void max_heapify(int A[], int length, int i){ 2 int l, r; 3 int largest, temp; 4 while(i <= length){ 5 l = 2 * i; 6 r = 2 * i + 1; 7 8 if(l <= length && A[l] > A[i]) 9 largest = l; 10 else 11 largest = i; 12 if(r <= length && A[r] > A[largest]) 13 largest = r; 14 15 if(largest != i){ 16 temp = A[largest]; 17 A[largest] = A[i]; 18 A[i] = temp; 19 i = largest; 20 } 21 else 22 break; 23 } 24 }
7.證明:對於一個大小為n的堆,max_heapify的最壞運行時間為Ω(lgn)。(提示:對於n個節點的堆,恰當地設置每個節點的值,使得從根節點到葉節點的路徑上的每個節點都遞歸調用max_heapify)。
假設大小為n的堆的高度為h
首先如何使得從根節點到葉節點的路徑上的每個節點都遞歸調用max_heapify?每次調用后的子樹都符合子樹的根節點小於左右兒子節點,則最初的根節點的值比所有左右兒子的值都小。接着要考慮的就是路徑-----如何使得從根節點到葉節點的路徑更長(最壞的情況)?“堆數據結構是一種數組對象,可以視為一棵完全二叉樹。樹的每一層都是填滿的,最后一層除外(最后一層從一個節點的左子樹開始填)”------《算導》原文。因此每次根節點與其左兒子節點交換,路徑會是最長的,則所有左子樹的值大於或等於右子樹。這樣設置,max_heapify會調用h次,所以運行時間為Θ(h),即Θ(lgn)。根據大Θ符號的定義(鏈接為維基),詳細見算導第三章----函數的增長,因此的得最壞運行時間為Ω(lgn)。
8.證明:在任一含n個元素的堆中,最多有ceiling(n / 2^(h+1))個高度為h的節點。
證明為算導的答案:(答案很詳細)
9.對於一個其所有n個元素已按遞增序排列的數組A,堆排序的運行時間是多少?若A的元素呈降序呢?
遞增序排列和遞減序(降序)一樣:先建堆,運行時間為O(n),然后將數組最后一個與第一個交換,數組大小減一,在進行max_heapify(維持最大堆的性質),依次重復操作(O(n) * O(lgn) = O(n*lgn))
因此運行時間為O(n) * O(lgn) = O(n*lgn)
10.證明:堆排序的最壞情況運行時間為Ω(n*lgn)。
堆排序屬於比較排序,比較排序的最壞情況運行時間的下界為Ω(n*lgn),見算導第八章第一節。
11.證明:在所有元素都不相同時,堆排序的最佳運行時間為Ω(n*lgn)。
無論數組是否已排序,都要調用n - 1次max_heapify函數,可以得最佳運行時間為Ω(n*lgn)。