# 【5-2】排序演算法 & sort 排序,在日常生活中也很常見,國小老師會教我們照身高矮到高排路隊,或者是座號小到大。 我們舉最簡單的例子,座號從小到大排序,假設班上現在有 $n$ 位學生,目前隨機排成一個路隊,要怎麼讓他們按照一個「規則」來排成小到大呢? ## 1. 選擇排序(Selection Sort) **原理**:每輪找出最小值,放到前面的位置。 * 時間複雜度:$O(n^2)$ * 空間複雜度:$O(1)$ ```cpp void selectionSort(int a[], int n) { for (int i = 0; i < n - 1; i++) { int minIndex = i; // 假設目前的為最小值,紀錄 index for (int j = i + 1; j < n; j++) { if (a[j] < a[minIndex]) { // 從自己往後,發現比目前更小的就記錄 index minIndex = j; } } swap(a[i], a[minIndex]); // 交換 } } ``` ### Python對照 ```python= def selectionSort(a): for i in range(len(a) - 1): min_index = i for j in range(i + 1, len(a)): if a[j] < a[min_index]: min_index = j a[i], a[min_index] = a[min_index], a[i] ``` --- ## 2. 氣泡排序(Bubble Sort) **原理**:相鄰兩數比較,大的往後冒泡,逐輪排序。 * 時間複雜度:$O(n^2)$,最佳 $O(n)$ * 空間複雜度:$O(1)$ ```cpp void bubbleSort(int a[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (a[j] > a[j + 1]) { // 相鄰兩數比較,若該位大於下一位,則兩者交換位置 swap(a[j], a[j + 1]); } } } } ``` ### Python對照 ```python= def bubbleSort(a): for i in range(len(a) - 1): for j in range(len(a) - 1 - i): if a[j] > a[j + 1]: a[j], a[j + 1] = a[j + 1], a[j] ``` --- ## 3. 插入排序(Insertion Sort) **原理**:每次將一個數插入前面已排序的區段。 * 時間複雜度:$O(n^2)$,最佳 $O(n)$ * 空間複雜度:$O(1)$ ```cpp void insertionSort(int a[], int n) { for (int i = 1; i < n; i++) { int key = a[i]; // 紀錄目前要排序的資料 int j = i - 1; while (j >= 0 && a[j] > key) { // 確保邊界條件,以及嘗試尋找第一個小於 key 的位置 a[j + 1] = a[j]; // 挪動一格,供 key 插入用 j--; } a[j + 1] = key; // 插入 key } } ``` ### Python對照 ```python= def insertionSort(a): for i in range(1, len(a)): key = a[i] j = i - 1 while j >= 0 and a[j] > key: a[j + 1] = a[j] j -= 1 a[j + 1] = key ``` --- ## 4. 希爾排序(Shell Sort) **原理**:先比較距離較遠的元素(gap),再逐漸縮小距離至 1,最終完成排序。是插入排序的改良版。 * 時間複雜度:$O(n^{1.3})$(視 gap 策略而定) * 空間複雜度:$O(1)$ ```cpp void shellSort(int a[], int n) { for (int gap = n / 2; gap > 0; gap /= 2) { // 每次砍半(二分) for (int i = gap; i < n; i++) { int key = a[i]; // 要插入的數字 int j = i - gap; while (j >= 0 && a[j] > key) { a[j + gap] = a[j]; // 往右移動 j -= gap; } a[j + gap] = key; // 插入 key } } } ``` ### Python對照 ```python= def shellSort(a): n = len(a) gap = n // 2 while gap > 0: for i in range(gap, n): key = a[i] j = i while j >= gap and a[j - gap] > key: a[j] = a[j - gap] j -= gap a[j] = key gap //= 2 ``` --- ## 5. 快速排序(Quick Sort) **原理**:選一個 pivot,把比它小的放左邊,比它大的放右邊,再對左右兩邊遞迴排序。 * 時間複雜度:$O(n \log n)$,最壞 $O(n^2)$ * 空間複雜度:$O(\log n)$ ```cpp int partition(int a[], int left, int right) { int pivot = a[right]; // 選最後一個當 pivot int i = left - 1; for (int j = left; j < right; j++) { if (a[j] < pivot) { i++; swap(a[i], a[j]); // 把小於 pivot 的換到左邊 } } swap(a[i + 1], a[right]); // 把 pivot 放中間正確位置 return i + 1; // 回傳 pivot 的位置 } void quickSort(int a[], int left, int right) { if (left < right) { int pi = partition(a, left, right); // 劃分陣列 quickSort(a, left, pi - 1); // 左邊遞迴排序 quickSort(a, pi + 1, right); // 右邊遞迴排序 } } ``` ### Python對照 ```python= def partition(a, left, right): pivot = a[right] i = left - 1 for j in range(left, right): if a[j] < pivot: i += 1 a[i], a[j] = a[j], a[i] a[i + 1], a[right] = a[right], a[i + 1] return i + 1 def quickSort(a, left, right): if left < right: pi = partition(a, left, right) quickSort(a, left, pi - 1) quickSort(a, pi + 1, right) ``` --- ## 6. 合併排序(Merge Sort) **原理**:把陣列一分為二,分別排序後,再將兩部分合併。 * 時間複雜度:$O(n \log n)$ * 空間複雜度:$O(n)$ ```cpp void merge(int a[], int l, int m, int r) { int n1 = m - l + 1, n2 = r - m; int L[n1], R[n2]; // 左右兩個子陣列 for (int i = 0; i < n1; i++) L[i] = a[l + i]; for (int i = 0; i < n2; i++) R[i] = a[m + 1 + i]; int i = 0, j = 0, k = l; while (i < n1 && j < n2) { if (L[i] <= R[j]) a[k++] = L[i++]; else a[k++] = R[j++]; } while (i < n1) a[k++] = L[i++]; // 左邊剩的補上 while (j < n2) a[k++] = R[j++]; // 右邊剩的補上 } void mergeSort(int a[], int l, int r) { if (l < r) { int m = (l + r) / 2; // 中點 mergeSort(a, l, m); mergeSort(a, m + 1, r); merge(a, l, m, r); } } ``` ### Python對照 ```python= def merge(a, l, m, r): L = a[l:m+1] R = a[m+1:r+1] i = j = 0 k = l while i < len(L) and j < len(R): if L[i] <= R[j]: a[k] = L[i] i += 1 else: a[k] = R[j] j += 1 k += 1 a[k:r+1] = L[i:] + R[j:] def mergeSort(a, l, r): if l < r: m = (l + r) // 2 mergeSort(a, l, m) mergeSort(a, m + 1, r) merge(a, l, m, r) ``` --- ## 7. 堆積排序(Heap Sort) **原理**:先建成最大堆,每次把最大值(根節點)拿出來放到最後,重建堆。 * 時間複雜度:$O(n \log n)$ * 空間複雜度:$O(1)$ ```cpp void heapify(int a[], int n, int i) { int largest = i; int l = 2 * i + 1, r = 2 * i + 2; if (l < n && a[l] > a[largest]) largest = l; if (r < n && a[r] > a[largest]) largest = r; if (largest != i) { swap(a[i], a[largest]); heapify(a, n, largest); // 遞迴調整子樹 } } void heapSort(int a[], int n) { for (int i = n / 2 - 1; i >= 0; i--) heapify(a, n, i); // 建堆 for (int i = n - 1; i >= 0; i--) { swap(a[0], a[i]); // 最大值移到最後 heapify(a, i, 0); // 重建堆 } } ``` ### Python對照 ```python= def heapify(a, n, i): largest = i l = 2 * i + 1 r = 2 * i + 2 if l < n and a[l] > a[largest]: largest = l if r < n and a[r] > a[largest]: largest = r if largest != i: a[i], a[largest] = a[largest], a[i] heapify(a, n, largest) def heapsort(a): n = len(a) for i in range(n // 2 - 1, -1, -1): heapify(a, n, i) for i in range(n - 1, 0, -1): a[0], a[i] = a[i], a[0] heapify(a, i, 0) ``` --- ## 8. 計數排序(Counting Sort) **原理**:計算每個數字出現的次數,依序重建陣列。 * 時間複雜度:$O(n + k)$,$k$ 為最大數值 * 空間複雜度:$O(n + k)$ ```cpp void countingSort(int a[], int n) { int maxVal = *max_element(a, a + n); int count[maxVal + 1] = {0}; for (int i = 0; i < n; i++) count[a[i]]++; // 統計次數 int index = 0; for (int i = 0; i <= maxVal; i++) { while (count[i]--) a[index++] = i; // 根據次數填回去 } } ``` ### Python對照 ```python= def countingsort(a): if not a: return maxVal = max(a) count = [0] * (maxVal + 1) for x in a: count[x] += 1 index = 0 for i, c in enumerate(count): for _ in range(c): a[index] = i index += 1 ``` --- 看了這麼多,頭要暈了吧,我也覺得很累,所以我們用 C++ STL 提供的 `sort()` 函式來排序吧: ## sort **原理**:C++ STL 提供的 `sort()` 使用的是一種混合排序(IntroSort),結合了 **快速排序、堆積排序與插入排序** 的優點。 * 時間複雜度:平均 $O(n \log n)$,最壞 $O(n \log n)$ * 空間複雜度:$O(\log n)$ ```cpp sort(a.begin(), a.end()); // 升冪排序 sort(a.begin(), a.end(), greater<int>()); // 降冪排序 sort(a.begin(), a.end(), compare); // 使用自定義的排序(寫在副函式) ``` ```cpp bool compare(int a, int b) { return a > b; // a 大於 b 時返回 true,實現降冪排序 } ``` 注意!如果是 C 陣列,就要使用指標的寫法,不能使用迭代器,可以參考以下寫法: ```cpp sort(a, a + n); // 對 a[0] ~ a[n-1] 升冪排序 ``` ### Python對照 ```python= a = [5, 3, 8, 1] # 升冪排序 a.sort() # 降冪排序 a.sort(reverse=True) # 使用 sorted() 函式(不改變原串列) b = sorted(a) # 自訂排序(以字串長度排序為例) words = ['apple', 'banana', 'cherry', 'date'] words.sort(key=len) ``` --- 聯絡方式:codecodefunny@gmail.com 最後編修時間:2025/08/24 子柚筆