遞迴是程式設計中極具表達力的技巧,但「能用遞迴不代表該用遞迴」。本文以階乘與費波那契數列為例,實測遞迴與迭代的效能差異,並分析遞迴的隱性成本。
long factorial_recursive(int n) {
if (n <= 0) return 1;
return n * factorial_recursive(n - 1);
}
long factorial_iterative(int n) {
long result = 1;
while (n > 1) {
result *= n;
n--;
}
return result;
}
long fibonacci_recursive(int n) {
if (n <= 2) return 1;
return fibonacci_recursive(n-1) + fibonacci_recursive(n-2);
}
呼叫樹呈二元樹,節點數 ≈ 2^n,大量重複計算(如 fib(3) 被計算多次)。
long fibonacci_iterative(int n) {
if (n <= 2) return 1;
long first = 1, second = 1, result = 0;
while (n > 2) {
n--;
second = first;
first = result;
result = first + second;
}
return result;
}
僅需 O(n) 時間、O(1) 空間,保留前兩項即可推導下一項。
long fib_memo(int n, long memo[]) {
if (n <= 2) return 1;
if (memo[n]) return memo[n];
return memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo);
}
加上備忘錄後遞迴也能達 O(n),但仍有函式呼叫開銷與堆疊風險。
| 實作方式 | 時間複雜度 | 空間複雜度 | 相對執行時間 |
|---|---|---|---|
| 遞迴費波那契 | O(2^n) | O(n) 堆疊 | 極慢(秒級) |
| 遞迴+備忘錄 | O(n) | O(n) 堆疊+陣列 | 快(毫秒級) |
| 迭代費波那契 | O(n) | O(1) | 最快(微秒級) |
| 遞迴階乘 | O(n) | O(n) 堆疊 | 中等 |
| 迭代階乘 | O(n) | O(1) | 最快 |
| ✅ 適合遞迴 | ❌ 慎用遞迴 |
|---|---|
| 樹/圖遍歷(DFS)、分治演算法(快排、合併排序) | 線性遞推可用迭代替代(階乘、費波那契、陣列求和) |
| 問題自然具備遞迴結構(漢諾塔、括號匹配) | 深度不可控或可能極大(鏈表反轉、深層巢狀結構) |
| 尾端遞迴且編譯器支援優化 | 嵌入式/即時系統:堆疊大小受限 |
遞迴是把雙面刃:表達力強但效能弱。階乘與費波那契是教學經典,但實務上**階乘請用迭代,費波那契請用迭代或矩陣快速冪(O(log n))**。寫程式前先問自己:「這裡用遞迴的好處,能否抵銷其付出的堆疊與呼叫成本?」