從鍵盤任意輸入一組數, 比如:3216549870。要求對它進行排序,使它順序排列。
我理解的堆排序思路如下:
NO.1 首先想着讓這組數按下面這種方式形成完全二叉樹樹型結構。
A
我先給出這棵完全二叉樹所具備的一些基本性質:
a: 不管這組數是奇數個還是偶數個,假設總數為count,倒數第一個非葉子節點的下標總是 count / 2 - 1。 (你可以列舉奇數個試一下)
b: 葉子節點下標都大於count / 2 - 1,小於count。
c: 左孩子的下標 = 父節點下標 * 2 + 1。右孩子的下標 = 父節點下標 * 2 + 2。
NO.2 接着通過調整上面的樹來構造大根堆(所有父節點的值都比其左右孩子值大的完全二叉樹),步驟如下(若題目要求逆序排列,則構造小根堆,兩者思路很相似):
第一步:對樹A的倒數第一層處理 。先比較0和5的值,5大於0,則不動;接着比較8和7,8大於7,用8(8與7中更大的)和6比較,8大於6,交換8和6,這樣交換是為了把盡可能大的數往上移動。在交換完畢后,6處於葉子節點位置,最后一層便調整完畢了,得到下面樹B:(此步其實處理了下標為4和3的元素,使其分別形成大根堆,沒明白先往下看)
A
B
第二步:對樹B倒數第二層進行處理。先比較4和9,9大於4,用9和1比較,9大於1,交換9和1;接着比較8和5,8大於5,用8和2比較,8大於2,交換8和2,因為在8和2交換后,2處於非葉子節點位置,所以應將6,7中更大的那個數和2比較,7大於2,交換7和2,交換完畢后,2處於葉子節點位置,本層便處理完畢。得到下面這棵樹:(此步其實處理了下標為2和1的元素,使其分別形成大根堆)
C
第三步:處理第二層(對第2步調整后的樹處理),比較8和9,9大於8,用9和3比較,9大於3,交換9和3,此時,因為在9和3交換后,3處在非葉子節點位置,所以應用4(4與1中更大的)和3比較,4大於3,交換4和3,交換完畢后,3處於葉子節點位置,本層調整完畢。得到下面這棵樹:(此步其實處理了下標為0的元素,使其形成大根堆)
這棵樹便成了大根堆。 上面便是構造大根堆的過程。
NO.3 因為在上面這大根堆中,9(這組數中最大的)的位置恰好是下標為0的,交換9和最末尾的0,便把最大值放到最末尾了。
NO.4 接着把上面這棵樹繼續調整為大根堆(9不用考慮),調整思路很簡單:從下標為0的位置,即0所處位置開始,往下調整,直到調整到葉子節點為止。
調整過程: 比較8和4,8大於4,用8和0比較,8大於0,交換8和0,因為8和0交換,0處在非葉子節點位置,所以應用7(7,5中更大的那個數)和0比較,7大於0,交換7和0,交換完畢后,0仍處在非葉子節點位置,繼續往下比較,最終得到下面這棵樹(也是大根堆):
接着交換8(下標為0的)和2(倒數第二個),再忽略8,繼續構造大根堆,大根堆構造好了交換下標為0的和倒數第三個,依次循環,直到可忽略的數字為這組數總數減1時停下,此時,你會發現這組數已經順序排列了!
現在我希望你能把這堆排序的整個思路串起來想一下。
第一步:形成樹(當然此步不需要任何操作)。
第二步:構造大根堆。
第三步:交換下標為0和末尾的,交換后使末尾往前移動一位(--count 實現)。
第四步:從下標為0的往下調整,繼續形成大根堆。
第五步:重復第三步和第四部,直到末尾移動到下標為0處,便順序了。
稍加思考發現,思路可以更簡便些,上面第二步可以修改為:停留在第一次剛好形成大根堆的前一步(即將要調整下標為0的那個節點),然后緊接着做第四步,然后做第三步,再四三四三步循環。
現在最終思路出來了!
第一步:形成樹(當然此步不需要任何操作)。
第二步:停留在構造成功大根堆的前一步。
第三步:從下標為0的往下調整,形成大根堆。
第四步:交換下標為0和末尾的,交換后使末尾往前移動一位(--count 實現)。
第五步:重復第三步和第四部,直到末尾移動到下標為0處,便順序了。
開始貼代碼了:
#include <stdio.h> #include <malloc.h>
void adjuctHeap(int i, int *arr, int count); void heapSort(int *arr, int count); void adjuctHeap(int i, int *arr, int count) {
int tmp; int max; while(i <= count / 2 - 1) { // 條件判斷檢測是否到了葉子節點 tmp = 2 * i + 2 >= count ? 0 : arr[2 * i + 2]; //總數為偶數,最后一個父節點沒有右孩子 max = arr[2 * i + 1] >= tmp ? 2 * i + 1 : 2 * i + 2; //max值:左右孩子中更大的那個孩子節點的下標 if(arr[max] > arr[i]) { tmp = arr[max]; arr[max] = arr[i]; arr[i] = tmp; i = max; } else
break; } } void heapSort(int *arr, int count) { int i; int tmp;
for(i = count / 2 - 1; i > 0; i--) { // 停留在構造成功大根堆的前一步。 count / 2 - 1 表示倒數第一個非葉子節點。 adjuctHeap(i, arr, count); } while(count > 1) { adjuctHeap(0, arr, count); // 構造大根堆 tmp = arr[0]; arr[0] = arr[count - 1]; // 交換 arr[count - 1] = tmp; --count; // 末尾往前移動 } } int main() { int i; int *arr; int count; printf("please input the count of numbers:"); scanf("%d", &count); arr = (int *) calloc(sizeof(int), count); printf("please input the numbers:"); for(i = 0; i < count; i++) { scanf("%d", &arr[i]); } heapSort(arr, count); for(i = 0; i < count; i++) { printf("%d ", arr[i]); } printf("\n"); free(arr); return 0; }
結果
please input the count of numbers:10
please input the numbers:3 2 1 6 5 4 9 8 7 0
0 1 2 3 4 5 6 7 8 9