天天刷leetcode——509. 斐波那契数

题目描述

斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
Leetcode 509: https://leetcode-cn.com/problems/fibonacci-number/.

解题思路

1. 暴力递归

    def fib(self, N: int) -> int:
        if N ==1 or N ==2 :
            return 1
        return fib(N-1)+fib(N-2)

结论:这个方法十分低效,时间复杂度为O(2**n),存在大量重复计算的过程比如:
f(20) = f(18) + f(19) # 需要计算f(18)
f(19) = f(18) + f(17) # 还是需要计算f(18)

递归的时间复杂度如何求解?:
子问题个数*解决一个子问题需要的时间复杂度。该题的子问题个数为递归树中的节点总数,子问题的时间复杂度为O(1)

2. 重叠子问题求解

从上述分析,暴力递归存在大量的重复计算,这样的问题就是重叠子问题。因为有重复计算所以可以写一个列表或者字典,将以前计算过的存下来。

 def fib(self, N: int) -> int:
        def help(memo,n):
            if n == 1 or n == 2:
                return 1
            if memo[n] != 0:
                return memo[n]
            memo[n] = help(memo,n-1)+help(memo,n-2)
            return memo[n]
        if N<1:
            return 0
        memo = [0 for i in range(N+1)]
        return help(memo,N)

在这里插入图片描述
总结:这样就把很多重复计算少了很多,极大得减少了子问题。这样叫做自顶向下的方式。时间复杂度为 O(n)

3. 动态规划

除了自顶向下,也可以采用自底向上的方式。先算f(1),f(2)… 慢慢算到f(N)。

    def fib(self, N: int) -> int:
        if N == 1 or N==2:
            return 1
        if N ==0 :
            return 0
        pre = 1
        pre2 = 1
        for i in range(3,N+1):
            cur = pre+pre2
            pre = pre2
            pre2 = cur
        return cur

在这里插入图片描述
总结: 此时的状态转移方程为:dp[i] = dp[i-1] + dp[i-2].时间复杂度O(n), 空间复杂度O(1)。

动态规划的三要素:

  • 重叠子问题
  • 最优子结构
  • 状态转移矩阵

动态规划问题的一般形式就是求最值,求解动态规划的核心就是穷举。因为要求最值。但是动态规划的最值有点特殊,因为这类问题存在重叠子问题,如果暴力穷举效率会比较低。所以需要存储来优化结构。动态规划一定具备最优子结构,这样才能通过子问题的最值得到原问题的最值。

但是fib中并没有最优子结构,严格来说fib并不是严格的动态贵规划问题,没的最值。

references

[1] Leetcode 509: https://leetcode-cn.com/problems/fibonacci-number/.
[2] labuladong的算法小抄: https://labuladong.gitbook.io/algo/dong-tai-gui-hua-xi-lie/dong-tai-gui-hua-xiang-jie-jin-jie

猜你喜欢

转载自blog.csdn.net/qq_30516823/article/details/107559002