动态规划(一)—— Fibonacci 斐波那契

导读:本篇文章讲解 动态规划(一)—— Fibonacci 斐波那契,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com


1. 题目

🟠 题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第 n 项(从 0 开始,第 0 项为 0)。

在这里插入图片描述

🟠 基础框架

C++ 版本给出的基础框架代码如下:

class Solution {
public:
    int Fibonacci(int n) {

    }
};

🟠 原题链接

JZ10 斐波那契数列
 
509. 斐波那契数

2. 解题报告

对于斐波那契的求解方法,相信大家不会陌生,我这里给出了三种方法。

🟠 方法一:递归

斐波那契数列的定义:

F

(

n

)

=

F

(

n

1

)

+

F

(

n

2

)

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

F(n)=F(n1)+F(n2)

n

>

=

2

n

N

(n>=2,n∈N*)

n>=2nN,其中

F

(

1

)

=

1

F

(

2

)

=

1

F(1)=1,F(2)=1

F(1)=1F(2)=1

代码实现:

class Solution {
public:
    int Fibonacci(int n) {
        //n小于0,直接返回0
        if (n < 0) {
            return 0;
        }

        //第一项和第二项都是1
        if (n == 1 || n == 2) {
            return 1;
        }
        
        // F(n)=F(n-1)+F(n-2)
        return Fibonacci(n - 2) + Fibonacci(n - 1);
    }
};

🟣 复杂度分析

递归的方法时间复杂度为

O

(

2

n

)

O(2^n)

O(2n),随着 n 的增大呈现指数增长,效率低下。

当输入比较大时,可能导致栈溢出,而且在递归过程中有大量的重复计算。

n = 5 的时候,Fib(1) 就已经 5 次参与运算,Fib(2) 就已经 3 次参与运算,Fib(3) 被重复计算了 2 次,可想而知,当 n 越变越大时,重复计算的量就会大量增加,计算的速度会随着 n 的增加肉眼可见的变慢,程序甚至会挂。

在这里插入图片描述

因此,若是将每次计算出的小问题存起来就可以很好地提高效率,我们可以准备一个数组来存放小问题的结果。

🟠 方法二:动态规划

动态规划的基本方法在第一篇文章中已经说过了,首先我们来看一下分析过程:

(1)状态定义:Fib(i) 的值,即斐波那契数列的第 i 项的值。

(2)状态转移方程:Fib(i) = Fib(i-1) + Fib(i-2)

(3)状态的初始化:Fib(0) = 0Fib(1) = 1

(4)返回结果:F(n)

代码实现:

class Solution {
  public:
    int Fibonacci(int n) {
        // 初始值
        if (n <= 0) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return 1;
        }
        // 申请一个数组,保存子问题的解,题目要求从第0项开始
        int* record = new int[n + 1];
        record[0] = 0;
        record[1] = 1;
        for (int i = 2; i <= n; i++) {
            // F(n)=F(n-1)+F(n-2)
            record[i] = record[i - 1] + record[i - 2];
        }
        return record[n];
        delete[] record;
    }
};

🟣 复杂度分析

上述解法的时间复杂度为

O

(

n

)

O(n)

O(n),空间复杂度为

O

(

n

)

O(n)

O(n)

🟠 方法三:动态规划改良

其实 Fib(n) 只与它相邻的前两项有关,所以没有必要保存所有子问题的解,只需要保存两个子问题的解就可以。

所以我们也没必要额外开辟新的空间,我们只需要知道第 n 项的前 2 项值即可,直接定义一个 result 用来在循环的过程中不断更新 n - 1n - 2 为斐波那契数的值即可。

代码示例

class Solution {
  public:
    int Fibonacci(int n) {
        // 初始值
        if (n <= 0) {
            return 0;
        }
        if (n == 1 || n == 2) {
            return 1;
        }
		
        int fn1 = 1; //第1个数
        int fn2 = 1; //第2个数
        int ret = 0;
        for (int i = 3; i <= n; ++i) {
            // F(n)=F(n-1)+F(n-2)
            ret = fn2 + fn1;
            // 更新中间状态
            fn1 = fn2;
            fn2 = ret;
        }
        return ret;
    }
};

3. 总结

用动态规划来求解斐波那契数,那么可以达到最大的优化,时间复杂度:

O

(

n

)

O(n)

O(n),空间复杂度:

O

(

1

)

O(1)

O(1)

我们可以看出动态规划对于具有重叠子问题的应用具有很大的作用,不过这道题不用我们去找初始状态、边界状态、转移方程,是属于简单的题。

对于难题,如何简单的定义状态和状态如何转移是一个难点。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/80851.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!