美文网首页前端面试工程师
斐波那契数列传统递归思路到动态规划

斐波那契数列传统递归思路到动态规划

作者: 弱冠而不立 | 来源:发表于2020-11-19 15:24 被阅读0次

    推荐文章:动态规划套路详解

    传统思路:

    fib(0) = 0; fib(1) = 1; fib(n) = fib(n-1) + fib(n-2)
    这是每个算法课老师,讲递归的经典例题。但是leetcode上其实是把这个当作递归的反面教材的。
    原因就在于(举个例子):当计算 fib(8) 时,你要计算 f(7) 和 f(6),你要计算 f(7) 时,你又要计算f(6) 和 f(5),这就意味着存在大量的重复计算。

    传统思路 + 备忘录

    这里备忘录的意思就是,开辟一个数组,把每次计算的结果都存进去,如果这个备忘录数组中有了,则直接拿出来。举个例子:当计算好 f(6) 时,把 f(6) 存到 memory[6] 中,然后当计算 f(7) 时,就把备忘录里的 memory[6] 拿出来

    var fib = function(n, memory=[]) {
        // 递归出口
        if(n < 2) {
            return n;
        }
    
        //如果以前没计算过就把计算的推到备忘录
        if(!memory[n]) {
            memory[n] = fib(n-1, memory) + fib(n-2, memory)
        }
        // 否则备忘录里有数据直接返回备忘录里的数据
        return memory[n];
    }
    

    备忘录为主体的循环迭代

    根据上面的备忘录思想,我们可以把备忘录单独抽离出来,即 memory[i] 是依赖 memory[i-1] + memory[i-2]

    var fib = function(n) {
        let dp = []
        dp[0] = 0;
        dp[1] = dp[2] = 1
        for(let i=3; i<=n; i++) {
            dp[i] = dp[i-1] + dp[i-2]
        }
        return dp[n]
    };
    

    动态规划思路

    斐波那契的状态转移方程

    上面几种解法的核心思想都是这个状态转移方程:如 fib(n) = fib(n-1) + fib(n-2);dp[i] = dp[i-1] + dp[i-2]。但是其实我们不需要存储那么多状态,只需要存储前两个状态就可以了。根据这个思路,我们可以将空间复杂度降到O(1)

    var fib = function(n) {
        if(n<2) {
            return n;
        }
        // 相当于 fib(0)
        let preVal = 0; 
        // 相当于 fib(1)
        let curVal = 1;
        for(let i=2; i<=n; i++) {
            // 相当于 fib(2) = fib(1) + fib(0)
            let sum = curVal + preVal;
            // 把preVal设为fib(1)
            preVal = curVal;
            // 把curVal设为fib(2)
            curVal = sum;
            // 即下次循环的时候 sum = fib(1) + fib(2), 此时sum就相当于 fib(3)了
        }
        return curVal;
    };
    

    这样一步步下来,现在我们解斐波那契数列的算法的时间复杂度为 O(n),空间复杂度为 O(1)

    相关文章

      网友评论

        本文标题:斐波那契数列传统递归思路到动态规划

        本文链接:https://www.haomeiwen.com/subject/jivkiktx.html