實 習 內 容 |
|
目 標 |
練習以遞迴方式實作 "選擇排序法 (selection sort)" |
1 |
問題: 將整數陣列中存放的資料由小到大重新排列 |
2 |
選擇排序法分析: 以下圖為例, 陣列中存放 n 筆資料 (array[0], ..., array[n-1]), 首先一一比對得到其中的最大元素, 並且與陣列中最後一個元素 array[n-1] 互換, 如此就只剩下前面 n-1 筆資料需要排序了, 如此繼續把最大的元素挑出來, 讓問題的大小逐步減低, 直到所有元素由小到大排好為止。 |
3 |
遞迴演算法設計: 由上面的演算法描述中可以看到問題的困難度逐步降低, 這很適合運用遞迴方法來設計程式, 在設計遞迴時一般需要專注於 選擇排序的遞迴演算法: 1. if n is 1 2. the array is sorted else 3. move the largest element to the last of the array 4. sort the subarray which excludes the last array element (array[0] .. array[n-2]). |
4 |
程式實作: place_largest: (step 3) void place_largest(int array[], /* input/output - array in which to place largest */ int n) /* input - number of array elements to consider */ { int temp, /* temporary variable for exchange */ j, /* array subscript and loop control */ max_index; /* index of largest so far */ /* Save subscript of largest array value in max_index */ max_index = n - 1; /* assume the last element is the largest */ for (j = n - 2; j >= 0; --j) if (array[j] > array[max_index]) max_index = j; /* Unless the largest value is already the last element, exchange the largest and the last elements */ if (max_index != n - 1) { temp = array[n - 1]; array[n - 1] = array[max_index]; array[max_index] = temp; } } select_sort: (step 4) void select_sort(int array[], /* input/output - array to sort */ int n) /* input - number of array elements to sort */ { if (n > 1) { place_largest(array, n); select_sort(array, n - 1); } } |
5 |
疊代式選擇排序法實作: 根據步驟 2 直接以兩層迴圈實作選擇排序法 swap 函數: void swap(int *x, int *y) { int tmp; tmp = *x; *x = *y; *y = tmp; } printArrayContents 函數: void printArrayContents(int data[], int ndata) { int i; printf("Sorted data:\n"); for (i=0; i<ndata; i++) printf(" %d", data[i]); printf("\n"); } main 函數以及 swap 及 printArrayContents 函數原型: #include <stdio.h> #include <stdlib.h> void swap(int *, int *); void printArrayContents(int [], int); int main(void) { int data[] = {12, 3, 37, 8, 24, 15, 5, 33}; int ndata = sizeof(data) / sizeof(int); int i, j; int max; for (i=0; i<ndata; i++) { max = 0; for (j=1; j<ndata-i-1; j++) if (data[j]>data[max]) max = j; swap(&data[max], &data[ndata-i-1]); } printArrayContents(data, ndata); system("pause"); return 0; } 注意: swap(&data[max], &data[ndata-i-1]) 可以用 swap(data+max, data+ndata-i-1) 取代, 兩者效果完全一樣 |
6 |
遞迴以及疊代程式修改: 請修改步驟 4 的遞迴演算法以及步驟 5 的疊代演算法, 選擇演算法的功能維持由小到大排序資料, 但是如下圖所示, 演算法先找出陣列中最小的元素, 將其與陣列中第一個元素交換, 一步一步完成所有資料的排序: 疊代式演算法的修改比較直接 遞迴式演算法的修改會遇見一個小問題: 如何將陣列第 2 個元素到第 n 個元素當成一個陣列傳遞給函數處理? 其實將陣列傳遞給函數時基本上只是把陣列的起始位址傳遞給函數, 例如: 以 printArrayContents(data, ndata) 呼叫 printArrayContents(int array[], int size); 函數時, 函數中 array 陣列變數其實就是 data 陣列, main() 函數中只是把 data 陣列的起始位址交給 printArrayContents() 函數, 如果在 main() 函數中呼叫 printf("%p", data); 看到的資料和在 printArrayContents() 函數中呼叫 printf("%p", array); 會完全一樣; 所以要把 data[1] 到 data[n-1] 這些元素當成陣列傳遞給函數, 只要寫 printfArrayContents(&data[1], ndata-1) 即可; printArrayContents() 函數宣告的時候參數也可以寫 int printArrayContents(int *array, int size), 在 printArrayContents() 函數中使用 array 的程式碼完全不需要改變; 更進一步如果宣告為 int printArrayContents(int *const array, int size) 則可以防止在 printArrayContents() 函數中不小心修改 array 指標的內容; 如果宣告為 int printArrayConents(const int *const array, int size) 則可以防止在 printArrayContents() 函數中不小心修改 array 陣列的內容。 |