數據結構:遞歸:斐波那契數列(Fibonacci Sequence)

目錄

什么是斐波那契數列?

用遞歸推導Fibonacci

復雜度分析

?用迭代推導Fibonacci

?復雜度分析

?遞歸優化:記憶化遞歸(Memoized Recursion)?

復雜度分析


什么是斐波那契數列?

斐波那契數列(Fibonacci Sequence)是這樣一個序列:

從第 2 項開始,每一項等于前兩項之和。

F(0) = 0
F(1) = 1
F(2) = 1        ← 0 + 1
F(3) = 2        ← 1 + 1
F(4) = 3        ← 1 + 2
F(5) = 5        ← 2 + 3
F(6) = 8        ← 3 + 5
F(7) = 13       ← 5 + 8
F(8) = 21       ← 8 + 13
...

用遞歸推導Fibonacci

我們用自然語言重新表述遞歸的核心思想:

要計算第 n 項,只需遞歸調用第 n?1 項和第 n?2 項的計算,直到我們知道結果的那一刻(也就是 n==0 或 n==1)為止。

所以遞歸的思想是:

  1. 邊界條件(Base Case):當 n == 0 或 n == 1,直接返回 n 本身。

  2. 遞歸調用:否則,就返回 fib(n - 1) + fib(n - 2)

int fib(int n) {if (n <= 1) return n;         // base casereturn fib(n - 1) + fib(n - 2); // recursion
}

復雜度分析

我們以 fib(5) 為例,畫出調用樹結構?

                      fib(5)/      \fib(4)        fib(3)/     \        /     \fib(3)     fib(2)  fib(2)  fib(1)/     \     /   \   /   \fib(2)   fib(1) fib(1) fib(0) fib(1)/   \
fib(1) fib(0)

📈 時間復雜度?

T(n) = T(n-1) + T(n-2) + O(1)

其中:

  • T(n-1):遞歸求 fib(n-1)

  • T(n-2):遞歸求 fib(n-2)

  • O(1):加法操作 + 1 次返回?

?所以 T(n) 的增長趨勢就是 Fibonacci 的大小增長趨勢,即:

T(n) ≈ O(2?)

🔍 你可以這么理解這個復雜度

在最差情況下,調用樹是一個二叉樹

  • 高度:n

  • 節點數 ≈ 2?(滿二叉樹)

  • 每個節點代表一次函數調用 → 所以總時間就是 O(2?)

?故時間復雜度是 O(2?)

復雜度說明
時間復雜度O(2?),因為調用樹呈指數爆炸,每次展開兩個分支
空間復雜度O(n),因為遞歸棧最深可達 n 層


?用迭代推導Fibonacci

核心問題:如何“推進到下一步”?

我們寫下第一項和第二項:?

step 0: 0   → 這是 F(0)
step 1: 1   → 這是 F(1)

從 step 2 開始,每一步我們都:

  1. 把上面兩個數加起來,得出當前項

  2. 把這兩個數“往前移動”,以備下一次使用

因此,我們要用變量表示這個“移動窗口”,讓它們“滑動”到下一項:(有點類似于SQL中的窗口滑動)

我們只要兩個變量:

a = 0;   // 表示 F(n - 2)
b = 1;   // 表示 F(n - 1)

我們用第三個變量:

next = a + b;  // 當前項 F(n)

之后我們把窗口向前滑動:

a = b;
b = next;

?這三步形成了一個“更新循環”。

完整代碼:

思考邏輯結構

  • 需要處理:

    • n == 0:返回 0

    • n == 1:返回 1

  • i = 2 開始循環,到 i == n

  • 使用變量 a, b, next 來存儲 F(n-2), F(n-1), F(n)

#include <iostream>
using namespace std;int fib_iterative(int n) {if (n == 0) return 0;  // 基礎情況if (n == 1) return 1;  // 基礎情況int a = 0;  // 表示 F(0)int b = 1;  // 表示 F(1)int next;   // 用來存當前項 F(n)for (int i = 2; i <= n; ++i) {next = a + b;  // 當前項a = b;         // 向前滑動b = next;      // 向前滑動}return next;  // 最終 next 是 F(n)
}

?復雜度分析

??時間復雜度(Time Complexity)

看看上面這段代碼里,哪些操作會隨著 n 增長而重復?

  • for (int i = 2; i <= n; ++i)
    這個循環會執行 (n - 1) 次(從 i=2 到 i=n)

  • 每次循環內,做了以下操作:

    • next = a + b;

    • a = b;

    • b = next;

這些都是常數時間操作,每次循環都執行固定次數。

? 結論:

  • 循環執行 n - 1 次,每次是 O(1)

  • 所以:時間復雜度為 O(n)

🗃?空間復雜度(Space Complexity)?

我們在函數中定義了:

  • int a = 0;

  • int b = 1;

  • int next;

無論 n 多大,我們始終只使用這 3 個變量來計算 Fibonacci 數列。

我們沒有使用數組、沒有遞歸棧,也沒有任何隨著 n 增長而變大的數據結構。

? 結論:

  • 內存使用是常數級別

  • 所以:空間復雜度為 O(1)


?遞歸優化:記憶化遞歸(Memoized Recursion)?

在遞歸實現中,你會發現一個規律:很多調用是重復的!

例如在fib(5)中

  • fib(3) 被調用了 2 次

  • fib(2) 被調用了 3 次

  • fib(1) 被調用了 5 次

這就是遞歸 Fibonacci 的最大問題 —— 重疊子問題!這是一種指數級的浪費。

? 第一性解法:我們應該記住已經算過的結果。?

💡如何實現“記住”?

我們用一種最基本的數據結構 —— 數組,開一個數組 memo[] 存儲結果。

邏輯流程:

我先檢查 memo[n] 是否已經存有值:

  • 如果有,就直接返回它;

  • 如果沒有,就計算出來,并把結果存進去。

一步一步改造原始遞歸函數

原始遞歸(無記憶):

int fib(int n) {if (n == 0) return 0;if (n == 1) return 1;return fib(n - 1) + fib(n - 2);
}

? 加入“備忘錄數組”(記憶)

const int MAX = 1000;
int memo[MAX];  // 初始化為 -1 表示未計算void init_memo() {for (int i = 0; i < MAX; ++i) {memo[i] = -1;}
}int fib(int n) {if (memo[n] != -1) return memo[n];  // 如果已經算過了if (n == 0) return memo[0] = 0;if (n == 1) return memo[1] = 1;// 沒算過,就遞歸計算,并存入 memomemo[n] = fib(n - 1) + fib(n - 2);return memo[n];
}

剛剛實現的其實是“Top-Down 動態規劃”

雖然我們沒有提任何術語,但你用的是一種“從上往下遞歸,同時緩存子結果”的方法。

如果你用迭代而非遞歸,那就是“Bottom-Up 動態規劃”

復雜度分析

項目原始遞歸記憶化遞歸
時間復雜度O(2?)(指數級)O(n)(每個 n 只算一次)
空間復雜度O(n)(遞歸棧)O(n)
核心優化原理消除重復計算每個子問題只解一次

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87086.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87086.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87086.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ArcGIS Pro利用擦除工具,矢量要素消除另一矢量部分區域

選擇“System Toolboxes”→“Analysis Tools.tbx”→“Overlay”→“Erase&#xff08;擦除&#xff09;”。 原始 擦除后

Linux: network: 性能 pause

最近看到一個問題,是關于網卡的throughput的性能問題,后來在ethtool-S里看到有pause的counter,這個也是網絡性能問題的一個分析方向。算是學到了新的知識點。 $ grep -i -e 2025- -e pause ethtool*ens2f1np1 | grep -v -e ": 0\$" | headtail 4====

目標檢測系列(五)已標注數據集(yolo格式)導入labelstudio繼續標注

目錄 1、labelstudio安裝 2、yolo(txt)轉json 3、COCO轉yolo(僅針對coco格式標注信息) 4、設置環境變量并啟動labelstudio 5、進入label studio創建工程并設置任務標簽 6、安裝http-server并啟動文件映射服務 7、進入label studio導入json文件即可 1、labelstudio安裝 …

pytorch底層原理學習--Libtorch

libtorch libtorch 是 PyTorch 的 C 實現版本&#xff0c;可以認為所有的pytorch底層都是由c實現&#xff0c;而pytorch的所有C實現就叫libtorch&#xff0c;也就是我們在pytorch官網getstart頁面下載的cpytorch版本。我們用python寫的pytorch神經網絡代碼都會通過pybind11將p…

TCP 三次握手協商 MSS 前,如何確定 MSS 值(附 Linux 內核源碼)

文章目錄 一、SYN總結影響 SYN MSS 的因素 二、SYNACK總結影響 SYNACK MSS 的因素 結合 Linux 內核源碼 一、SYN 總結影響 SYN MSS 的因素 套接字選項 TCP_MAXSEG路由選項 advmss出口 MTU 減去 40(TCP 和 IP 的固定首部大小)IPV4_MAX_PMTU - 40(同上) 二、SYNACK 總結影響 SY…

掃描電子顯微鏡(SEM)夏令營面試基礎題及答案

第二期表征問題SEM&#xff0c;后續會陸續更新其他表征 SEM和XRD一樣&#xff0c;都是表征里面很常見的手段&#xff0c;基本上看論文這兩個都是必不可少的 對于這部分內容&#xff0c;理解記憶&#xff1e;死記硬背&#xff0c;到時會問起來回答個大概就行&#xff0c; 像上…

Leetcode力扣解題記錄--第49題(map)

題目鏈接&#xff1a;49. 字母異位詞分組 - 力扣&#xff08;LeetCode&#xff09; 題目描述 給你一個字符串數組&#xff0c;請你將 字母異位詞 組合在一起。可以按任意順序返回結果列表。 示例 1: 輸入: strs ["eat", "tea", "tan", &quo…

AI賦能智慧餐飲:Spring Boot+大模型實戰指南

? 餐飲行業三大痛點 高峰期點餐擁堵&#xff1a;300人餐廳&#xff0c;15個服務員仍排長隊 后廚浪費嚴重&#xff1a;食材損耗率高達25%&#xff0c;成本失控 顧客體驗同質化&#xff1a;復購率不足30% &#x1f680; 智慧餐飲解決方案架構 &#x1f525; 核心模塊代碼實現…

用鴻蒙打造真正的跨設備數據庫:從零實現分布式存儲

網羅開發 &#xff08;小紅書、快手、視頻號同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企業從事人工智能項目研發管理工作&#xff0c;平時熱衷于分享各種編程領域的軟硬技能知識以及前沿技術&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

【Docker基礎】Docker數據卷:數據卷的作用與使用場景

目錄 1 Docker數據卷概述 1.1 什么是數據卷 1.2 數據卷的核心特性 3 數據卷與綁定掛載的對比 2.1 技術對比 2.2 選擇建議 3 數據卷的核心作用 3.1 數據持久化 3.2 數據共享 3.3 備份與遷移 4 數據卷使用場景詳解 4.1 數據庫應用 4.2 日志集中管理 5 數據卷操作全…

安裝GPU版本的Pytorch

前言 Pytorch是深度學習框架,在工作中我們一般是使用GPU版本的Pytorch,提高運行效率 安裝GPU版本的Pytorch需要先安裝CUDA和CUANN這兩個GPU環境 如果準備安裝GPU版本的Pytorch安裝同志沒有安裝CUDA和CUANN,請看我上一篇文章 RTX5070顯卡安裝CUDA和CUDNN-CSDN博客 目錄 安裝…

微信小程序學習筆記

微信小程序學習筆記 一、文件和目錄結構介紹 小程序包括&#xff1a;主體文件、頁面文件 主體文件&#xff1a; app.js&#xff1a;小程序入口文件app.json&#xff1a;小程序的全局配置文件app.wxss&#xff1a;小程序的全局樣式 頁面文件&#xff1a;是每個頁面所需的文…

抓包之通過wireshark抓ping包

寫在前面 本文看下如何抓ping包。 1&#xff1a;正文 因為ping使用的是icmp協議&#xff0c;所以這里我們可以通過過濾icmp協議來進行抓包&#xff1a; 其中對于icmp請求報文狀態碼是8&#xff0c;如下&#xff1a; 響應狀態碼是0&#xff1a; 如下圖是一個局域網環境中…

大文件分片上傳 — nodejs

上傳文件路由&#xff1a; var express require(express); var router express.Router(); const multer require(multer); const fs require(fs); const path require(path);// 確保上傳目錄存在 const uploadDir path.join(__dirname, ../backend/uploads); const temp…

HarmonyOS File和base64字符串轉換

1. HarmonyOS File和base64字符串轉換 1.1. Base64 1.1.1. Base64認知 Base64 是一種基于64個 ASCII 字符來表示二進制數據的表示方法&#xff0c;這個64個不同的字符為&#xff1a; ??&#xff08;1&#xff09;大、小寫字母&#xff08;A– Z、a–z&#xff09;。52個 ?…

【NodeJs】【npm】npm安裝electron報錯

解決問題 npm安裝electron報錯一般來說是鏡像源的問題。 electron的鏡像源與一般的 vue 之類的鏡像源地址不一樣需要單獨配置。 npm讀取的全局配置一般是在 C:\Users\{用戶}\.npmrc 這個配置文件中。 如果你找不到你的配置文件可以執行如下命令, # 執行后會直接用txt打開你的…

植物small RNA靶基因預測軟件,psRobot

psRoto軟件安裝 網址 http://omicslab.genetics.ac.cn/psRobot/downloads.php下載和安裝 wget http://omicslab.genetics.ac.cn/psRobot/program/WebServer/psRobot_v1.2.tar.gz # tar -zxvf psRobot_v1.2.tar.gz # cd psRobot_v1.2 ## ./configure make make installpsRot…

翻譯服務器

基于UDP編程博客里的回顯服務器代碼,翻譯服務只需要改process方法即可 所以我們可以創建一個UdpDictServer直接繼承UdpEchoServer然后重寫process方法 在重寫的方法中完成翻譯的過程 代碼: package network;import java.io.IOException; import java.net.SocketException; …

初等變換 線性代數

初等變換 介紹了三種初等變換的操作。 初等矩陣 初等矩陣是干嘛的呢&#xff1f;實際上初等矩陣就是我們矩陣的初等操作&#xff0c;每一個對矩陣的初等變換操作都相當于乘上一個初等矩陣。 左乘初等矩陣就相當于對行進行初等操作&#xff0c;右乘則相當于對列進行初等操作。…

Java基礎 集合框架 隊列架構 雙端隊列 Deque

雙端隊列 Deque Deque 方法簡介Deque 核心特點Deque實現類 ArrayDequeArrayDeque 構造方法ArrayDeque 的數據結構及實現原理ArrayDeque 方法介紹ArrayDeque 核心特性ArrayDeque 總結ArrayDeque 使用樣例代碼 Deque實現類 LinkedListDeque實現類 ConcurrentLinkedDeque (非阻塞線…