遞迴的奧秘:從費波那契數列到動態規劃的演算法升級之路

更新於 發佈於 閱讀時間約 7 分鐘

軟體工程師職涯升級計畫啟動!立即預約職涯諮詢、履歷健檢或模擬面試👈,為您的加薪做好準備!


遞迴函式(Recursive Function)是一種在函式內部呼叫自己的技術,它讓複雜的問題能被分解成更小的、重複的子問題來解決。其中一個最經典的範例就是費波那契數列

費波那契數列是什麼?

費波那契數列(Fibonacci Sequence)是一個特殊的數列,從 0 和 1 開始,後續的每一項都是前兩項的和。它的數學定義如下:

  • 第 0 項:F(0)=0
  • 第 1 項:F(1)=1
  • 第 n 項:F(n)=F(n−1)+F(n−2)

根據這個規則,我們可以列出數列的前幾項:0,1,1,2,3,5,8,13,21,34,55,...

遞迴程式碼實作

將上述數學定義轉換成 JavaScript 程式碼非常直觀。我們可以建立一個 fib 函式來計算第 n 項費波那契數。

function fib(n) {
// 終止條件(Base Case):
// 當 n 為 0 或 1 時,直接返回其定義值
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}

// 遞迴呼叫:將問題拆解成兩個較小的子問題
return fib(n - 1) + fib(n - 2);
}

// 範例:計算第 5 項
console.log(fib(5)); // 輸出 5

這個程式碼的核心是兩個部分:

  1. 遞迴條件(Recursive Step):return fib(n - 1) + fib(n - 2),這讓函式不斷地呼叫自己,將問題分解。
  2. 終止條件(Base Case):if (n === 0) 和 if (n === 1),這兩個條件是遞迴停止的時機。如果沒有終止條件,遞迴將會無限循環下去,導致程式崩潰。

遞迴呼叫的流程:以樹狀圖解析

raw-image

當我們呼叫 fib(5) 時,它會像樹一樣層層展開。

從圖中可以看到,每次函式呼叫都會繼續往下分解,直到遇到 fib(1)fib(0) 這些終止條件。一旦達到終點,程式會從最底層開始,將結果層層往上傳遞並加總,最終得到 fib(5) 的結果。這個從下往上、由內而外計算的過程,正是因為函式呼叫是藉由**函式堆疊(Call Stack)來管理的,其遵循後進先出(Last In, First Out, LIFO)**的原則。


動態規劃:告別重複計算的低效率

你可能已經注意到,傳統的遞迴解法雖然直觀,但效能極差。以費波那契數列為例,當我們計算 fib(5) 時,fib(3) 被計算了兩次,fib(2) 甚至被計算了三次。這種對相同子問題的重複計算,正是導致時間複雜度高達 O(2n) 的主因。

動態規劃(Dynamic Programming, DP)正是為了解決這類問題而生的強大技術。其核心思想很簡單:「把已經計算過的部分儲存下來!」

動態規劃的適用條件

一個問題若要能用動態規劃解決,通常需要滿足以下三個性質:

  1. 最優子結構(Optimal Substructure):一個問題的最優解,是由其子問題的最優解所構成。例如,要求費波那契數列第 n 項的最佳解,只需知道第 n−1 和第 n−2 項的最佳解。
  2. 無後效性(No Aftereffect):當前狀態的確定,不會受到後續決策的影響。也就是說,我們在計算第 n 項時,只需依賴第 n−1 和第 n−2 項的結果,而它們是如何被計算出來的,對我們來說並不重要。
  3. 重疊子問題(Overlapping Subproblems):問題可以被分解成重複的子問題。費波那契數列就是一個典型的例子,fib(3) 和 fib(2) 在計算 fib(5) 的過程中多次被呼叫。

動態規劃解法:兩種常見模式

動態規劃通常有兩種實現方式,核心都是利用**記憶化(Memoization)**來避免重複計算。

raw-image

1. 自上而下(Top-down)遞迴 + 記憶化

這種方式依然使用遞迴,但會額外使用一個陣列或雜湊表來儲存已經計算過的結果。在每次遞迴呼叫前,先檢查結果是否已被計算過。

// 使用陣列來儲存計算結果
const memo = [];

function fibDP_topDown(n) {
// 如果結果已經被計算過,直接返回
if (memo[n] !== undefined) {
return memo[n];
}

// 終止條件
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}

// 遞迴計算並將結果存入 memo
memo[n] = fibDP_topDown(n - 1) + fibDP_topDown(n - 2);

return memo[n];
}

console.log(fibDP_topDown(5)); // 輸出 5
// 這時 memo 陣列會長這樣:[0, 1, 1, 2, 3, 5]

2. 自下而上(Bottom-up)迭代

這種方式通常使用**迴圈(Iteration)**來從最底層的子問題開始計算,並逐步向上推導。

function fibDP_bottomUp(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}

// 使用陣列來儲存計算結果
const dp = [0, 1];

// 從第 2 項開始,逐步計算到第 n 項
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}

return dp[n];
}

console.log(fibDP_bottomUp(5)); // 輸出 5

無論是哪種方式,透過儲存中間結果,我們將時間複雜度從指數級的 O(2n) 大幅優化到線性的 O(n),這正是動態規劃的強大之處。


結語

遞迴動態規劃是演算法領域中兩個核心且相輔相成的概念。遞迴提供了一個優雅且直觀的解題思路,讓我們能夠將大問題分解為小問題,但其潛在的效能問題不容忽視。而動態規劃則是在遞迴的基礎上進行優化,透過記憶化來避免重複計算,將低效的指數級複雜度提升到高效的線性或多項式級,是解決許多複雜問題的強大武器。掌握這兩種技巧,將能讓你更有效率地應對各種程式設計挑戰。

留言
avatar-img
留言分享你的想法!
avatar-img
跨越國界的程式人生
2會員
35內容數
自學程式,現為網頁開發工程師,同時擔任線上課程講師,專注於幫助自學程式的開發者找到理想工作。熱愛技術與分享,致力於將複雜的概念轉化為實用知識,讓更多人踏入軟體開發的世界。
2025/09/01
軟體工程師職涯諮詢、履歷健檢與模擬面試服務,助您加薪!文章深入淺出圖 (Graph) 資料結構,涵蓋有向圖、無向圖、鄰接矩陣、鄰接列表、LeetCode 實戰範例 (Number of Islands),助您提升演算法能力。
Thumbnail
2025/09/01
軟體工程師職涯諮詢、履歷健檢與模擬面試服務,助您加薪!文章深入淺出圖 (Graph) 資料結構,涵蓋有向圖、無向圖、鄰接矩陣、鄰接列表、LeetCode 實戰範例 (Number of Islands),助您提升演算法能力。
Thumbnail
2025/08/23
本文提供軟體工程師面試常考的樹狀結構(Tree)相關知識,包含樹的基本概念、常見術語、二元樹、二元搜尋樹、樹的遍歷(DFS 與 BFS)、以及經典題目 Validate Binary Search Tree 的 JavaScript 解法與核心思路。
Thumbnail
2025/08/23
本文提供軟體工程師面試常考的樹狀結構(Tree)相關知識,包含樹的基本概念、常見術語、二元樹、二元搜尋樹、樹的遍歷(DFS 與 BFS)、以及經典題目 Validate Binary Search Tree 的 JavaScript 解法與核心思路。
Thumbnail
2025/07/29
什麼是雜湊表?—— 鍵值對的抽象資料型態 想像一下,你有一本非常厚的通訊錄。如果這本通訊錄是按照每個人的姓名首字母排序的,當你想找「王小明」的電話時,你可以迅速翻到「W」的開頭,然後快速找到他。雜湊表就
Thumbnail
2025/07/29
什麼是雜湊表?—— 鍵值對的抽象資料型態 想像一下,你有一本非常厚的通訊錄。如果這本通訊錄是按照每個人的姓名首字母排序的,當你想找「王小明」的電話時,你可以迅速翻到「W」的開頭,然後快速找到他。雜湊表就
Thumbnail
看更多
你可能也想看
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
2025 vocus 推出最受矚目的活動之一——《開箱你的美好生活》,我們跟著創作者一起「開箱」各種故事、景點、餐廳、超值好物⋯⋯甚至那些讓人會心一笑的生活小廢物;這次活動不僅送出了許多獎勵,也反映了「內容有價」——創作不只是分享、紀錄,也能用各種不同形式變現、帶來實際收入。
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
嗨!歡迎來到 vocus vocus 方格子是台灣最大的內容創作與知識變現平台,並且計畫持續拓展東南亞等等國際市場。我們致力於打造讓創作者能夠自由發表、累積影響力並獲得實質收益的創作生態圈!「創作至上」是我們的核心價值,我們致力於透過平台功能與服務,賦予創作者更多的可能。 vocus 平台匯聚了
Thumbnail
動態規劃Dynamic Programming其實是 一種泛用的演算法思考方式與演算法建構框架。 動態規劃並不拘束於只能解課本上特定的的範例題。 只要我們能找出DP狀態定義、DP遞迴結構、初始條件(終止條件),就能適用動態規劃來解題,以數學的形式表達,並且在紙筆上或者電腦上、計算機上計算
Thumbnail
動態規劃Dynamic Programming其實是 一種泛用的演算法思考方式與演算法建構框架。 動態規劃並不拘束於只能解課本上特定的的範例題。 只要我們能找出DP狀態定義、DP遞迴結構、初始條件(終止條件),就能適用動態規劃來解題,以數學的形式表達,並且在紙筆上或者電腦上、計算機上計算
Thumbnail
本文章討論如何使用動態規劃和回頭查看技巧來計算最長遞增子序列的長度,並提供了相關的測試案例和範例。本文還包括了詳細的演算法和程式碼示例,以及時間和空間複雜度的分析。
Thumbnail
本文章討論如何使用動態規劃和回頭查看技巧來計算最長遞增子序列的長度,並提供了相關的測試案例和範例。本文還包括了詳細的演算法和程式碼示例,以及時間和空間複雜度的分析。
Thumbnail
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 直線排列Permutations 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所有可能的情況,遞迴展開所有分
Thumbnail
這篇文章,會帶大家快速回顧DFS+回溯法框架(還沒看過或想複習的可以點連結進去)。 用DFS+回溯法框架,解開 直線排列Permutations 的全系列題目。 幫助讀者鞏固DFS+回溯法框架這個重要的知識點。 回顧 DFS+回溯法框架 白話的意思 # 列舉所有可能的情況,遞迴展開所有分
Thumbnail
這篇文章介紹了排列和組閤中的錯位排列和排容原理,並提供了一種相對樸實的解題方法。透過例子詳細解釋了選擇情況下的數學原理,讓讀者能夠理解並吸收。文章通過課堂上難以推敲的題目,提出了一個相對簡單的方式來解題。 圖片選自@pngtree
Thumbnail
這篇文章介紹了排列和組閤中的錯位排列和排容原理,並提供了一種相對樸實的解題方法。透過例子詳細解釋了選擇情況下的數學原理,讓讀者能夠理解並吸收。文章通過課堂上難以推敲的題目,提出了一個相對簡單的方式來解題。 圖片選自@pngtree
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
Thumbnail
這篇文章,會帶著大家複習以前學過的數列DP框架, 並且以費式數列、爬樓梯、骨牌拚接的應用與遞迴數列概念為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 數列DP與遞迴數列常見的形式 如果是遞迴數列,常常看到以函數型式表達
Thumbnail
分享在網路上看到的陣列題目。通常 for...of 的 value 是陣列中的每個值,那如果我們在迭代中對陣列操作會發生什麼事? 題目來源:https://x.com/_jayphelps/status/1774640511158022335?s=20
Thumbnail
分享在網路上看到的陣列題目。通常 for...of 的 value 是陣列中的每個值,那如果我們在迭代中對陣列操作會發生什麼事? 題目來源:https://x.com/_jayphelps/status/1774640511158022335?s=20
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News