题目描述

✅ 509. 斐波那契数

image-20260928203104353

image-20260928203104354

题意分析

斐波那契数列的下标从零开始,F(0) = 0、F(1) = 1,当 n >= 2 时,F(n) = F(n - 1) + F(n - 2)。给定下标 n,只需要返回这一项的数值,不需要输出整条数列。

初始两项决定了整个数列,不能把它们替换为其他题目的初值。本题的 n 不超过 30,按定义从小到大逐项计算即可,结果也在普通整数范围内。

解法:滚动变量递推

核心思路

[!blue]

直接按公式递归,会在计算相邻两项时反复求解许多相同的更小项。既然每一项只依赖更早的两项,就按下标递增计算:轮到某一项时,它所需的两个值已经得到,不必再次展开递归。

若用数组保存全部 F(0)..F(n),递推本身很直接,但计算 F(i) 时实际上只读取 F(i - 2) 和 F(i - 1),更早的项以后都不会再用到。因此只保留两个滚动变量 prev2 和 prev1 就足够。

在第 i 轮开始前,约定 prev2 = F(i - 2)、prev1 = F(i - 1)。先计算二者之和得到 F(i),然后把旧的 prev1 移给 prev2,把新结果移给 prev1。更新后两个变量正好保存 F(i - 1)、F(i),符合下一轮的要求。

循环从 i = 2 开始,所以初始值必须是 0 和 1。n 为零或一时根本不需要递推,直接返回 n;其他情况计算到第 n 项后,最新的 prev1 就是答案。

解题步骤

  1. 若 n < 2,直接返回 n,处理两个已知初始项。
  2. 初始化 prev2 = 0、prev1 = 1,分别代表第零项和第一项。
  3. 从 i = 2 到 n,先计算旧的两个变量之和。
  4. 将两个变量整体向前推进一项:prev2 接收旧 prev1,prev1 接收新和。
  5. 循环结束,返回保存第 n 项的 prev1。

代码实现

class Solution {
    public int fib(int n) {
        if (n < 2) {
            return n;
        }

        int prev2 = 0;
        int prev1 = 1;

        for (int i = 2; i <= n; i++) {
            // 先计算新项,再整体推进前两项,避免覆盖仍需读取的值。
            int current = prev2 + prev1;

            prev2 = prev1;
            prev1 = current;
        }

        return prev1;
    }
}
func fib(n int) int {
    if n < 2 {
        return n
    }

    prev2, prev1 := 0, 1
    for i := 2; i <= n; i++ {
        // 同时赋值读取两个旧状态,再整体推进到下一项。
        prev2, prev1 = prev1, prev2+prev1
    }
    return prev1
}

复杂度分析

  • 时间复杂度:O(n)。每个下标从二到 n 只计算一次,每轮执行常数次运算。
  • 空间复杂度:O(1)。仅保存相邻两项及当前和,不需要数组或递归调用栈。

关键点总结

[!green]

  • 递推顺序对应依赖顺序,先得到小下标项,再计算大下标项,避免重复求值。
  • 滚动变量能够替代数组,是因为下一项始终只依赖最近两项。
  • 初值、循环起点和返回变量必须共同符合每轮开始前的状态定义。

易错点总结

[!yellow]

  • 忽略 n = 0 的入口:不执行循环时若直接返回初始化的 prev1,会错误返回一。
  • 初始两项都设成一:这不符合本题从下标零开始的定义,后续数值都会偏移。
  • 先覆盖 prev2 再计算和:会丢掉旧的第 i - 2 项。Java 先保存 current;Go 的多重赋值会先计算所有右侧表达式。
  • 循环没有包含 n:只计算到 n - 1 会少求一项,条件应为 i <= n。
  • 返回 prev2:循环结束时它保存的是上一项,答案在 prev1 中。

相似题目

题目 难度 关联与区别
70. 爬楼梯 简单 递推关系相近但初值不同,不能因为都依赖前两项就直接共用返回值。
1137. 第 N 个泰波那契数 简单 把依赖前两项扩展为前三项,滚动变量的数量随依赖宽度变化。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2025/18200391
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!