言實(shí)現(xiàn)七大經(jīng)典排序算法詳解)
1. 數(shù)據(jù)結(jié)構(gòu)排序算法概述排序算法是計(jì)算機(jī)科學(xué)中最基礎(chǔ)也最重要的算法類別之一。作為一名C語(yǔ)言開(kāi)發(fā)者掌握常見(jiàn)的排序算法不僅能幫助我們更好地理解數(shù)據(jù)結(jié)構(gòu)還能在實(shí)際編程中根據(jù)具體場(chǎng)景選擇最優(yōu)的排序策略。本文將深入剖析七種經(jīng)典排序算法在C語(yǔ)言中的實(shí)現(xiàn)包括選擇排序、插入排序、希爾排序、堆排序、快速排序、歸并排序和計(jì)數(shù)排序。排序算法的核心任務(wù)是將一組無(wú)序的數(shù)據(jù)元素按照特定順序通常是升序或降序重新排列。不同的排序算法在時(shí)間復(fù)雜度、空間復(fù)雜度、穩(wěn)定性等方面各有特點(diǎn)。理解這些算法的實(shí)現(xiàn)原理和性能特征對(duì)于編寫高效、可靠的程序至關(guān)重要。提示在學(xué)習(xí)排序算法時(shí)建議同時(shí)關(guān)注算法的時(shí)間復(fù)雜度和空間復(fù)雜度這是評(píng)估算法效率的兩個(gè)關(guān)鍵指標(biāo)。2. 選擇排序的實(shí)現(xiàn)與優(yōu)化2.1 基本選擇排序原理選擇排序是最直觀的排序算法之一其基本思想是每次從待排序的數(shù)據(jù)元素中選出最小或最大的一個(gè)元素存放在序列的起始位置直到全部待排序的數(shù)據(jù)元素排完。void selectionSort(int arr[], int n) { for (int i 0; i n-1; i) { int min_idx i; for (int j i1; j n; j) { if (arr[j] arr[min_idx]) min_idx j; } // 交換找到的最小元素和第一個(gè)元素 int temp arr[min_idx]; arr[min_idx] arr[i]; arr[i] temp; } }選擇排序的時(shí)間復(fù)雜度為O(n2)因?yàn)樾枰M(jìn)行n-1輪比較每輪比較的次數(shù)遞減。雖然效率不高但選擇排序有一個(gè)顯著特點(diǎn)它的交換次數(shù)最少只有O(n)次交換操作。這在某些特定場(chǎng)景下如交換成本很高時(shí)可能是一個(gè)優(yōu)勢(shì)。2.2 選擇排序的優(yōu)化策略雖然選擇排序的基本實(shí)現(xiàn)很簡(jiǎn)單但我們?nèi)钥梢赃M(jìn)行一些優(yōu)化雙向選擇排序同時(shí)尋找最小和最大元素分別放在序列的兩端這樣每輪可以減少一半的迭代次數(shù)。使用哨兵減少比較次數(shù)在某些特定情況下可以通過(guò)設(shè)置哨兵來(lái)減少內(nèi)層循環(huán)的比較操作。提前終止如果在某一輪中沒(méi)有發(fā)生交換可以提前終止排序過(guò)程。void optimizedSelectionSort(int arr[], int n) { int left 0, right n - 1; while (left right) { int min_idx left, max_idx right; // 確保arr[min_idx] arr[max_idx] if (arr[min_idx] arr[max_idx]) { swap(arr[min_idx], arr[max_idx]); } for (int i left 1; i right; i) { if (arr[i] arr[min_idx]) { min_idx i; } else if (arr[i] arr[max_idx]) { max_idx i; } } swap(arr[left], arr[min_idx]); swap(arr[right], arr[max_idx]); left; right--; } }注意盡管進(jìn)行了優(yōu)化選擇排序的時(shí)間復(fù)雜度在最壞情況下仍然是O(n2)不適合處理大規(guī)模數(shù)據(jù)集。3. 插入排序的詳細(xì)實(shí)現(xiàn)3.1 基本插入排序算法插入排序的工作方式類似于我們整理?yè)淇伺频姆绞矫看螌⒁粋€(gè)待排序的元素插入到已排序序列中的適當(dāng)位置直到所有元素都插入完畢。void insertionSort(int arr[], int n) { for (int i 1; i n; i) { int key arr[i]; int j i - 1; // 將arr[0..i-1]中大于key的元素后移 while (j 0 arr[j] key) { arr[j 1] arr[j]; j--; } arr[j 1] key; } }插入排序在最好情況下數(shù)組已經(jīng)有序的時(shí)間復(fù)雜度為O(n)最壞和平均情況下為O(n2)。對(duì)于小規(guī)模數(shù)據(jù)或基本有序的數(shù)據(jù)插入排序表現(xiàn)良好這也是為什么它常被用作快速排序等高級(jí)算法的子過(guò)程。3.2 插入排序的優(yōu)化技巧二分查找插入在內(nèi)層循環(huán)中使用二分查找來(lái)確定插入位置可以減少比較次數(shù)但移動(dòng)元素的次數(shù)不變。希爾排序插入排序的改進(jìn)版本我們將在下一節(jié)詳細(xì)介紹。哨兵技巧設(shè)置哨兵元素來(lái)減少邊界檢查。void binaryInsertionSort(int arr[], int n) { for (int i 1; i n; i) { int key arr[i]; int left 0, right i - 1; // 二分查找插入位置 while (left right) { int mid left (right - left) / 2; if (arr[mid] key) { right mid - 1; } else { left mid 1; } } // 移動(dòng)元素 for (int j i - 1; j left; j--) { arr[j 1] arr[j]; } arr[left] key; } }在實(shí)際應(yīng)用中插入排序特別適合處理近乎有序的數(shù)據(jù)集。例如在某些增量排序場(chǎng)景中當(dāng)數(shù)據(jù)集已經(jīng)基本有序時(shí)插入排序的效率可以接近O(n)。4. 希爾排序的進(jìn)階分析4.1 希爾排序的基本原理希爾排序是插入排序的一種高效改進(jìn)版本也稱為縮小增量排序。它通過(guò)將原始列表分割成若干子列表來(lái)進(jìn)行插入排序隨著算法的進(jìn)行子列表的長(zhǎng)度逐漸增大最終整個(gè)列表變?yōu)橐粋€(gè)子列表。void shellSort(int arr[], int n) { // 初始間隔設(shè)為數(shù)組長(zhǎng)度的一半然后逐步縮小 for (int gap n/2; gap 0; gap / 2) { // 對(duì)每個(gè)子數(shù)組進(jìn)行插入排序 for (int i gap; i n; i) { int temp arr[i]; int j; for (j i; j gap arr[j - gap] temp; j - gap) { arr[j] arr[j - gap]; } arr[j] temp; } } }希爾排序的時(shí)間復(fù)雜度取決于間隔序列的選擇最好的情況下可以達(dá)到O(n log2 n)。雖然理論上不如快速排序或歸并排序高效但在實(shí)際應(yīng)用中希爾排序常常表現(xiàn)出色特別是對(duì)于中等大小的數(shù)組。4.2 希爾排序的間隔序列選擇希爾排序的性能很大程度上取決于間隔序列的選擇。常見(jiàn)的間隔序列有Shell原始序列n/2, n/4, ..., 1Hibbard序列1, 3, 7, 15, ..., 2^k-1Sedgewick序列1, 5, 19, 41, 109,...// 使用Hibbard序列的希爾排序?qū)崿F(xiàn) void shellSortHibbard(int arr[], int n) { // 生成Hibbard序列 int k 1; while ((1 k) - 1 n) k; k--; while (k 1) { int gap (1 k) - 1; for (int i gap; i n; i) { int temp arr[i]; int j; for (j i; j gap arr[j - gap] temp; j - gap) { arr[j] arr[j - gap]; } arr[j] temp; } k--; } }提示在實(shí)際應(yīng)用中Sedgewick序列通常能提供更好的性能但實(shí)現(xiàn)起來(lái)也更復(fù)雜。對(duì)于大多數(shù)情況簡(jiǎn)單的Shell原始序列已經(jīng)足夠好。5. 堆排序的深入理解5.1 堆數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)堆排序利用了堆這種數(shù)據(jù)結(jié)構(gòu)的特性。堆是一種特殊的完全二叉樹(shù)滿足堆性質(zhì)每個(gè)節(jié)點(diǎn)的值都大于或等于最大堆或小于或等于最小堆其子節(jié)點(diǎn)的值。// 調(diào)整堆使其滿足堆性質(zhì) void heapify(int arr[], int n, int i) { int largest i; // 初始化最大值為根節(jié)點(diǎn) int left 2 * i 1; // 左子節(jié)點(diǎn) int right 2 * i 2; // 右子節(jié)點(diǎn) // 如果左子節(jié)點(diǎn)大于根節(jié)點(diǎn) if (left n arr[left] arr[largest]) largest left; // 如果右子節(jié)點(diǎn)大于當(dāng)前最大值 if (right n arr[right] arr[largest]) largest right; // 如果最大值不是根節(jié)點(diǎn)交換并繼續(xù)堆化 if (largest ! i) { swap(arr[i], arr[largest]); heapify(arr, n, largest); } } // 堆排序主函數(shù) void heapSort(int arr[], int n) { // 構(gòu)建最大堆從最后一個(gè)非葉子節(jié)點(diǎn)開(kāi)始 for (int i n / 2 - 1; i 0; i--) heapify(arr, n, i); // 一個(gè)個(gè)從堆頂取出元素 for (int i n - 1; i 0; i--) { swap(arr[0], arr[i]); // 將當(dāng)前最大值移到數(shù)組末尾 heapify(arr, i, 0); // 對(duì)剩余元素重新堆化 } }堆排序的時(shí)間復(fù)雜度為O(n log n)這是比較排序算法的理論下限。堆排序是原地排序算法不需要額外的存儲(chǔ)空間這使得它在內(nèi)存受限的環(huán)境中特別有用。5.2 堆排序的應(yīng)用場(chǎng)景堆排序特別適合以下場(chǎng)景需要O(1)額外空間的排序場(chǎng)景需要同時(shí)獲取最大或最小幾個(gè)元素的場(chǎng)景需要優(yōu)先級(jí)隊(duì)列實(shí)現(xiàn)的場(chǎng)景實(shí)時(shí)系統(tǒng)因?yàn)槎雅判虻淖顗那闆r時(shí)間復(fù)雜度也是O(n log n)// 獲取數(shù)組中前k個(gè)最小元素 void getTopK(int arr[], int n, int k) { // 構(gòu)建大小為k的最大堆 for (int i k / 2 - 1; i 0; i--) heapify(arr, k, i); // 處理剩余元素 for (int i k; i n; i) { if (arr[i] arr[0]) { swap(arr[0], arr[i]); heapify(arr, k, 0); } } // 此時(shí)前k個(gè)元素就是最小的k個(gè)但不一定有序 // 如果需要有序可以對(duì)這k個(gè)元素進(jìn)行排序 }堆排序的一個(gè)缺點(diǎn)是它的緩存局部性較差因?yàn)樗谂判蜻^(guò)程中訪問(wèn)內(nèi)存的方式不太友好這可能導(dǎo)致在實(shí)際硬件上的性能不如快速排序。6. 快速排序的全面解析6.1 快速排序的基本實(shí)現(xiàn)快速排序是一種分治算法它選擇一個(gè)基準(zhǔn)元素將數(shù)組分為兩部分一部分小于基準(zhǔn)一部分大于基準(zhǔn)然后遞歸地對(duì)這兩部分進(jìn)行排序。// 分區(qū)函數(shù) int partition(int arr[], int low, int high) { int pivot arr[high]; // 選擇最后一個(gè)元素作為基準(zhǔn) int i (low - 1); // i是小于基準(zhǔn)的元素的索引 for (int j low; j high - 1; j) { if (arr[j] pivot) { i; swap(arr[i], arr[j]); } } swap(arr[i 1], arr[high]); return (i 1); } // 快速排序主函數(shù) void quickSort(int arr[], int low, int high) { if (low high) { int pi partition(arr, low, high); quickSort(arr, low, pi - 1); quickSort(arr, pi 1, high); } }快速排序的平均時(shí)間復(fù)雜度為O(n log n)最壞情況下當(dāng)數(shù)組已經(jīng)有序或逆序時(shí)會(huì)退化到O(n2)。然而通過(guò)合理選擇基準(zhǔn)元素可以大大降低最壞情況發(fā)生的概率。6.2 快速排序的優(yōu)化策略三數(shù)取中法選擇第一個(gè)、中間和最后一個(gè)元素的中值作為基準(zhǔn)減少最壞情況發(fā)生的概率。小數(shù)組切換到插入排序?qū)τ谛∫?guī)模子數(shù)組通常n10使用插入排序更高效。三向切分快速排序處理大量重復(fù)元素的情況。尾遞歸優(yōu)化減少遞歸深度。// 優(yōu)化的分區(qū)函數(shù)使用三數(shù)取中法 int optimizedPartition(int arr[], int low, int high) { // 三數(shù)取中 int mid low (high - low) / 2; if (arr[mid] arr[low]) swap(arr[low], arr[mid]); if (arr[high] arr[low]) swap(arr[low], arr[high]); if (arr[mid] arr[high]) swap(arr[mid], arr[high]); int pivot arr[high]; int i (low - 1); for (int j low; j high - 1; j) { if (arr[j] pivot) { i; swap(arr[i], arr[j]); } } swap(arr[i 1], arr[high]); return (i 1); } // 優(yōu)化的快速排序?qū)π?shù)組使用插入排序 void optimizedQuickSort(int arr[], int low, int high) { while (low high) { // 小數(shù)組使用插入排序 if (high - low 10) { insertionSort(arr low, high - low 1); break; } else { int pi optimizedPartition(arr, low, high); // 尾遞歸優(yōu)化先處理較小的子數(shù)組 if (pi - low high - pi) { optimizedQuickSort(arr, low, pi - 1); low pi 1; } else { optimizedQuickSort(arr, pi 1, high); high pi - 1; } } } }快速排序在實(shí)踐中通常是排序大規(guī)模數(shù)據(jù)集的首選算法因?yàn)樗钠骄阅芊浅:枚宜膬?nèi)循環(huán)非常緊湊在現(xiàn)代計(jì)算機(jī)體系結(jié)構(gòu)上表現(xiàn)良好。7. 歸并排序的經(jīng)典實(shí)現(xiàn)7.1 歸并排序的基本原理歸并排序是另一種采用分治策略的排序算法。它將數(shù)組分成兩半遞歸地對(duì)每一半進(jìn)行排序然后將兩個(gè)已排序的半部分合并成一個(gè)有序數(shù)組。// 合并兩個(gè)子數(shù)組的函數(shù) void merge(int arr[], int l, int m, int r) { int i, j, k; int n1 m - l 1; int n2 r - m; // 創(chuàng)建臨時(shí)數(shù)組 int L[n1], R[n2]; // 復(fù)制數(shù)據(jù)到臨時(shí)數(shù)組 for (i 0; i n1; i) L[i] arr[l i]; for (j 0; j n2; j) R[j] arr[m 1 j]; // 合并臨時(shí)數(shù)組回原數(shù)組 i 0; j 0; k l; while (i n1 j n2) { if (L[i] R[j]) { arr[k] L[i]; i; } else { arr[k] R[j]; j; } k; } // 復(fù)制剩余元素 while (i n1) { arr[k] L[i]; i; k; } while (j n2) { arr[k] R[j]; j; k; } } // 歸并排序主函數(shù) void mergeSort(int arr[], int l, int r) { if (l r) { int m l (r - l) / 2; mergeSort(arr, l, m); mergeSort(arr, m 1, r); merge(arr, l, m, r); } }歸并排序的時(shí)間復(fù)雜度為O(n log n)這是因?yàn)樗鼘?wèn)題分成兩半然后線性時(shí)間合并。歸并排序的一個(gè)主要優(yōu)點(diǎn)是它是穩(wěn)定的排序算法這在某些應(yīng)用中非常重要。7.2 歸并排序的優(yōu)化與變種自底向上的歸并排序非遞歸實(shí)現(xiàn)避免了遞歸調(diào)用的開(kāi)銷。原地歸并排序減少空間復(fù)雜度但實(shí)現(xiàn)復(fù)雜且性能可能下降。對(duì)小數(shù)組使用插入排序類似于快速排序的優(yōu)化。并行化歸并排序天然適合并行化處理。// 自底向上的歸并排序?qū)崿F(xiàn) void bottomUpMergeSort(int arr[], int n) { // 每次合并的子數(shù)組大小從1開(kāi)始每次翻倍 for (int curr_size 1; curr_size n-1; curr_size 2*curr_size) { // 選擇子數(shù)組的起始點(diǎn) for (int left_start 0; left_start n-1; left_start 2*curr_size) { int mid min(left_start curr_size - 1, n-1); int right_end min(left_start 2*curr_size - 1, n-1); merge(arr, left_start, mid, right_end); } } }歸并排序特別適合處理鏈表排序和外部排序數(shù)據(jù)太大無(wú)法全部加載到內(nèi)存的情況。在外部排序中歸并排序可以高效地合并已經(jīng)排序好的數(shù)據(jù)塊。8. 計(jì)數(shù)排序的特殊應(yīng)用8.1 計(jì)數(shù)排序的基本原理計(jì)數(shù)排序是一種非比較排序算法它通過(guò)統(tǒng)計(jì)每個(gè)元素出現(xiàn)的次數(shù)來(lái)實(shí)現(xiàn)排序。計(jì)數(shù)排序的時(shí)間復(fù)雜度為O(nk)其中k是輸入數(shù)據(jù)的范圍。void countingSort(int arr[], int n) { // 找到數(shù)組中的最大值 int max arr[0]; for (int i 1; i n; i) { if (arr[i] max) max arr[i]; } // 創(chuàng)建計(jì)數(shù)數(shù)組并初始化 int count[max1]; for (int i 0; i max; i) { count[i] 0; } // 存儲(chǔ)每個(gè)元素的計(jì)數(shù) for (int i 0; i n; i) { count[arr[i]]; } // 修改計(jì)數(shù)數(shù)組使其包含實(shí)際位置信息 for (int i 1; i max; i) { count[i] count[i-1]; } // 構(gòu)建輸出數(shù)組 int output[n]; for (int i n - 1; i 0; i--) { output[count[arr[i]] - 1] arr[i]; count[arr[i]]--; } // 將排序后的元素復(fù)制回原數(shù)組 for (int i 0; i n; i) { arr[i] output[i]; } }計(jì)數(shù)排序的局限性在于它只能用于整數(shù)排序并且當(dāng)數(shù)據(jù)范圍k很大時(shí)會(huì)消耗大量?jī)?nèi)存。然而當(dāng)k在合理范圍內(nèi)時(shí)計(jì)數(shù)排序的效率非常高。8.2 計(jì)數(shù)排序的適用場(chǎng)景計(jì)數(shù)排序特別適合以下場(chǎng)景數(shù)據(jù)范圍不大kO(n)需要穩(wěn)定排序的非負(fù)整數(shù)作為基數(shù)排序的子過(guò)程統(tǒng)計(jì)頻率分布// 優(yōu)化的計(jì)數(shù)排序處理有負(fù)數(shù)的情況 void countingSortWithNegative(int arr[], int n) { // 找到最小值和最大值 int max arr[0], min arr[0]; for (int i 1; i n; i) { if (arr[i] max) max arr[i]; if (arr[i] min) min arr[i]; } int range max - min 1; int count[range]; for (int i 0; i range; i) { count[i] 0; } for (int i 0; i n; i) { count[arr[i] - min]; } for (int i 1; i range; i) { count[i] count[i-1]; } int output[n]; for (int i n - 1; i 0; i--) { output[count[arr[i] - min] - 1] arr[i]; count[arr[i] - min]--; } for (int i 0; i n; i) { arr[i] output[i]; } }計(jì)數(shù)排序的一個(gè)有趣應(yīng)用是作為更復(fù)雜算法如后綴數(shù)組構(gòu)造的構(gòu)建塊。它也是理解更一般的桶排序和基數(shù)排序的基礎(chǔ)。9. 排序算法比較與選擇指南9.1 算法性能對(duì)比下表總結(jié)了七種排序算法的主要特性排序算法平均時(shí)間復(fù)雜度最壞時(shí)間復(fù)雜度空間復(fù)雜度穩(wěn)定性適用場(chǎng)景選擇排序O(n2)O(n2)O(1)不穩(wěn)定小規(guī)模數(shù)據(jù)交換成本高插入排序O(n2)O(n2)O(1)穩(wěn)定小規(guī)?;蚧居行驍?shù)據(jù)希爾排序O(n log n)O(n2)O(1)不穩(wěn)定中等規(guī)模數(shù)據(jù)堆排序O(n log n)O(n log n)O(1)不穩(wěn)定大規(guī)模數(shù)據(jù)內(nèi)存受限快速排序O(n log n)O(n2)O(log n)不穩(wěn)定大規(guī)模數(shù)據(jù)通用場(chǎng)景歸并排序O(n log n)O(n log n)O(n)穩(wěn)定大規(guī)模數(shù)據(jù)穩(wěn)定排序需求計(jì)數(shù)排序O(nk)O(nk)O(k)穩(wěn)定整數(shù)排序范圍小9.2 如何選擇合適的排序算法在實(shí)際編程中選擇排序算法時(shí)需要考慮以下因素?cái)?shù)據(jù)規(guī)模小規(guī)模數(shù)據(jù)n100可以使用簡(jiǎn)單排序插入、選擇大規(guī)模數(shù)據(jù)應(yīng)使用高級(jí)排序快速、歸并、堆。數(shù)據(jù)特性基本有序插入排序表現(xiàn)良好大量重復(fù)元素三向切分快速排序數(shù)據(jù)范圍小計(jì)數(shù)排序內(nèi)存限制內(nèi)存緊張時(shí)選擇原地排序算法堆排序、快速排序穩(wěn)定性需求需要穩(wěn)定排序時(shí)選擇歸并排序或插入排序?qū)崿F(xiàn)復(fù)雜度在時(shí)間允許的情況下簡(jiǎn)單算法更容易維護(hù)提示在C標(biāo)準(zhǔn)庫(kù)中qsort函數(shù)通常使用快速排序的某種變體實(shí)現(xiàn)。對(duì)于大多數(shù)通用排序需求直接使用庫(kù)函數(shù)是最佳選擇除非有特殊需求。10. 排序算法常見(jiàn)問(wèn)題與調(diào)試技巧10.1 常見(jiàn)錯(cuò)誤與解決方法數(shù)組越界訪問(wèn)原因循環(huán)條件或索引計(jì)算錯(cuò)誤解決方法仔細(xì)檢查循環(huán)邊界特別是遞歸算法的終止條件無(wú)限遞歸原因遞歸條件沒(méi)有正確更新解決方法確保每次遞歸調(diào)用都能使問(wèn)題規(guī)模減小排序不穩(wěn)定原因算法本身不穩(wěn)定或相等元素處理不當(dāng)解決方法選擇穩(wěn)定算法或修改比較邏輯性能不符合預(yù)期原因選擇了不適合數(shù)據(jù)特性的算法解決方法分析數(shù)據(jù)特征選擇合適的算法10.2 調(diào)試與測(cè)試技巧單元測(cè)試測(cè)試空數(shù)組測(cè)試單元素?cái)?shù)組測(cè)試已排序數(shù)組測(cè)試逆序數(shù)組測(cè)試包含重復(fù)元素的數(shù)組void testSortAlgorithm(void (*sortFunc)(int[], int)) { // 測(cè)試用例 int testCases[][10] { {}, // 空數(shù)組 {1}, // 單元素 {1,2,3,4,5}, // 已排序 {5,4,3,2,1}, // 逆序 {3,1,4,1,5,9,2,6}, // 隨機(jī) {2,2,2,2,2}, // 全相同 {-1,0,1,-2,2} // 含負(fù)數(shù) }; int sizes[] {0,1,5,5,5,8,5,5}; for (int i 0; i sizeof(sizes)/sizeof(sizes[0]); i) { sortFunc(testCases[i], sizes[i]); // 驗(yàn)證排序結(jié)果 for (int j 1; j sizes[i]; j) { assert(testCases[i][j-1] testCases[i][j]); } } }性能分析使用不同規(guī)模的數(shù)據(jù)測(cè)試運(yùn)行時(shí)間比較不同算法在實(shí)際數(shù)據(jù)上的表現(xiàn)使用性能分析工具定位熱點(diǎn)可視化調(diào)試打印排序過(guò)程中的數(shù)組狀態(tài)使用圖形化工具觀察排序過(guò)程在實(shí)際開(kāi)發(fā)中我經(jīng)常發(fā)現(xiàn)排序算法的錯(cuò)誤往往源于邊界條件的處理不當(dāng)。特別是在實(shí)現(xiàn)快速排序和歸并排序時(shí)遞歸終止條件和子數(shù)組范圍的確定需要格外小心。一個(gè)實(shí)用的技巧是在實(shí)現(xiàn)算法時(shí)先寫出明確的循環(huán)不變式或遞歸不變式然后在代碼中通過(guò)斷言來(lái)驗(yàn)證這些不變式是否始終保持。