当前位置: 首页 > article >正文

动态规划算法之斐波那契数列详细解读(附带Java代码解读)

1. 问题背景与递归的局限性

斐波那契数列的定义是:

F(n)=F(n−1)+F(n−2)

其中,F(0)=0,F(1)=1。

对于求解斐波那契数列的第 n 项,我们最直接的方式是用递归来表达:

public static int fibonacciRecursive(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;
    return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}

虽然递归看起来简单优雅,但这种方式的效率非常低。每次计算时,会重复地计算同样的子问题。比如在计算 F(5) 时,会多次计算 F(3)F(2)。这导致了时间复杂度为 O(2^n),增长极快,适用于较小的 n 值,但当 n 较大时,计算变得非常慢。

2. 动态规划的思想

动态规划的核心思想是避免重复计算,通过记忆化(Memoization)或自底向上(Bottom-Up)的方式来解决问题。斐波那契数列的问题可以被分解为很多子问题,而这些子问题之间有重叠。动态规划通过保存子问题的解,避免了重复计算。

动态规划的两个特点:
  1. 重叠子问题:大问题可以分解成若干小问题,而小问题的解会被多次使用。
  2. 最优子结构:一个问题的最优解可以由其子问题的最优解构成。

斐波那契数列具备这两个特性,因此可以通过动态规划来高效求解。

3. Java代码实现动态规划求解斐波那契数列

我们来看使用动态规划的实现:

public class FibonacciDP {
    public static void main(String[] args) {
        int n = 10; // 求第n个斐波那契数
        System.out.println("Fibonacci number at position " + n + " is: " + fibonacci(n));
    }

    public static int fibonacci(int n) {
        // 边界条件处理
        if (n == 0) {
            return 0; // F(0) = 0
        }
        if (n == 1) {
            return 1; // F(1) = 1
        }

        // 动态规划数组,用于存储中间结果
        int[] dp = new int[n + 1];
        dp[0] = 0; // F(0)
        dp[1] = 1; // F(1)

        // 自底向上计算斐波那契数列的值
        for (int i = 2; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2]; // 当前项由前两项之和得出
        }

        return dp[n]; // 返回第n个斐波那契数
    }
}

4. 详细解释每个部分

4.1 边界情况处理

斐波那契数列有两个特殊情况:

  • n = 0 时,返回 0
  • n = 1 时,返回 1

这两个条件在代码中通过以下部分处理:

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

这确保了程序能够正确处理最小的输入值。

4.2 动态规划数组

为了避免递归中的重复计算,我们引入一个数组 dp[] 来保存每一步计算的结果。数组的大小为 n+1,即它包含了从 F(0)F(n) 的所有结果。初始化数组中的前两个值:

int[] dp = new int[n + 1];
dp[0] = 0; // F(0)
dp[1] = 1; // F(1)

这里的 dp[0]dp[1] 对应斐波那契数列的前两项。

4.3 填充动态规划数组

接下来,通过一个 for 循环,从第3项(i=2)开始,依次计算每一个斐波那契数,并将结果存储到数组中。

for (int i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2]; // 递推公式 F(i) = F(i-1) + F(i-2)
}

这个循环的逻辑很简单:根据斐波那契数列的定义,第 i 项等于前两项之和,因此 dp[i] = dp[i - 1] + dp[i - 2]

4.4 返回结果

当循环结束后,数组 dp[n] 已经存储了第 n 个斐波那契数的值,我们只需将其返回:

return dp[n];

5. 时间复杂度与空间复杂度

时间复杂度: 整个过程我们只遍历了一次 dp[] 数组,从 i=2i=n,因此时间复杂度为 O(n),相比递归的 O(2^n),大大提升了效率。

空间复杂度: 由于我们使用了一个长度为 n+1 的数组,因此空间复杂度为 O(n)

6. 空间优化

虽然上面的解法已经非常高效,但还可以进一步优化空间复杂度。实际上,我们每次只需要用到前两项的值,因此可以不用数组,而是使用两个变量来代替 dp[] 数组。优化后的代码如下:

public static int fibonacciOptimized(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;

    int prev2 = 0; // F(0)
    int prev1 = 1; // F(1)
    int current = 0;

    for (int i = 2; i <= n; i++) {
        current = prev1 + prev2; // F(i) = F(i-1) + F(i-2)
        prev2 = prev1; // 更新 F(i-2)
        prev1 = current; // 更新 F(i-1)
    }

    return current; // 返回第n个斐波那契数
}

在这个版本中,我们只使用了三个变量来保存前两项的值以及当前项,从而将空间复杂度优化到了 O(1)

总结

  • 动态规划的关键是避免重复计算,通过保存子问题的结果提升效率。
  • 斐波那契数列的求解可以从递归优化为动态规划,从而将时间复杂度从 O(2^n) 降到 O(n)
  • 可以进一步优化空间复杂度,将 O(n) 降到 O(1),使用两个变量代替数组存储中间结果。

http://www.kler.cn/a/301108.html

相关文章:

  • 聚焦 NLP 和生成式 AI 的创新与未来 基础前置知识点
  • 第二十五章 TCP 客户端 服务器通信 - TCP 设备的 READ 命令
  • 【python】Bokeh 与 Plotly:创建交互式数据可视化工具
  • 【AI+教育】一些记录@2024.11.11
  • 网络协议之UDP
  • 【售前方案】工业园区整体解决方案,智慧园区方案,智慧城市方案,智慧各类信息化方案(ppt原件)
  • 陈坤2024行走的力量 走向山野感受距离自然更近的地方
  • 9月→2024年计算机与信息安全国际会议
  • 如何读.Net Framework 的源码?
  • 观众登记2025中国(深圳)国际智能手机供应链展览会
  • 数据分析与挖掘课程相关资源
  • 使用 Python 读取 Excel 数据的详细教程
  • 【C++】关键字、命名空间、输入和输出、缺省参数的深入了解
  • Flutter 使用第三方包加载3d模型
  • SpringTest框架JUnit单元测试用例获取ApplicationContext实例的方法
  • 【数据结构-一维差分】力扣1854. 人口最多的年份
  • 陪玩小程序源码搭建,基于PHP+MySQL陪玩系统app源码
  • 解码未来:H.265与H.266技术对比及EasyCVR视频汇聚平台编码技术优势
  • 工具篇之Apache Commons
  • LeetCode HOT100系列题解之数组中的第K个最大元素(7/100)
  • 【Python系列】理解 Python 中的时间和日期处理
  • 汽车智能座舱展︱2025 广州国际汽车智能座舱及车载显示技术展览会
  • python绘制3D瀑布图
  • springboot体会BIO(阻塞式IO)
  • C++——static应用全解
  • Java面试八股文