函數在其本體內直接或間接呼叫自身,稱為遞迴呼叫。遞迴函數必須包含兩要素:
n! = 1 (n = 0, 1) n! = n × (n-1)! (n > 1)
long factorial(int n) {
if (n < 0) {
printf("輸入錯誤:負數無階乘\n");
return -1;
}
if (n == 0 || n == 1) return 1; // 基礎情況
return n * factorial(n - 1); // 遞迴步驟
}
factorial(5)
5 * factorial(4)
4 * factorial(3)
3 * factorial(2)
2 * factorial(1)
1 ← 基礎情況,開始回溯
2 * 1 = 2
3 * 2 = 6
4 * 6 = 24
5 * 24 = 120
| 維度 | 遞迴 | 迭代 |
|---|---|---|
| 可讀性 | 數學定義直覺映射,代碼簡潔 | 需手動管理狀態變數 |
| 空間效能 | O(深度) 堆疊空間 | O(1) 常數空間 |
| 時間效能 | 函數呼叫開銷,可能重複計算 | 通常更快 |
| 適用場景 | 樹/圖遍歷、分治、回溯、動態規劃 | 線性累積、簡單迴圈 |
long factorial_iter(int n) {
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
// 尾遞迴:遞迴呼叫為最後一步操作
long fact_tail(int n, long acc) {
if (n <= 1) return acc;
return fact_tail(n - 1, n * acc); // 編譯器可優化為迴圈
}
-O2、MSVC /O2 可將尾遞迴優化為跳轉,消除堆疊增長。
long fib_memo(int n) {
static long memo[100] = {0};
if (n <= 1) return n;
if (memo[n]) return memo[n];
return memo[n] = fib_memo(n-1) + fib_memo(n-2);
}
ulimit -s、編譯器選項)。f(n-1) 已正確實現,思考如何用它構建 f(n)。遞迴是「以問題解決問題」的強大抽象,適合天生遞迴結構的問題。但 C 語言無保證尾呼叫優化,深度受限於堆疊。實務中:樹/圖/分治/回溯用遞迴,線性累積用迭代,重疊子問題加備忘錄,極深遞迴改迭代或顯式堆疊。理解遞迴本質,是邁向演算法設計的關鍵一步。