【JS 算法】动态规划入门

·  阅读 477
【JS 算法】动态规划入门

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

LvLin 最近在学习动态规划相关算法,讲道简单的动态规划入门题目,希望能给你带来启发。

斐波那契数列

「斐波那契数列」大家应该都挺熟悉。该数列的第 0 个数为 0,第 1 个数为 1,之后的每一个数都为前面两个数之和,用公式表示如下:

F(0) = 0F(1) = 1
F(n) = F(n - 1) + F(n - 2),n > 1
复制代码

很容易理解对吧,那让你写一个函数,根据参数 n,返回相应的斐波那契数,该怎么实现呢?

也很简单吧,根据上面的公式,用递归的方式实现,如下所示

var fib = function(n) {
    if (n <= 1) {
        return n;
    }

    return fib(n-1) + fib(n-2);
};
复制代码

让我们深入分析一下,假如 n 为 8,那么就要先计算出 fib(7)fib(6),而运行 fib(7) 的时候,又要先计算出 fib(6)fib(5),可以发现 fib(6) 被计算了两次。

用一棵树来表示一下递归的过程:

image.png

可以看到有很多重复的数字被计算。有什么办法可以优化一下吗?

假如我们把已经计算过的数字都保存起来,那是不是就可以避免重复计算了?参考代码如下所示:

var fib = function(n) {
    let arr = new Array(n+1); // 不要忘了 0
    arr[0] = 0;
    arr[1] = 1;

    function fibonacci(i) {
        if (arr[i] == undefined) {
            arr[i] = fibonacci(i-1) + fibonacci(i-2)
        }
        return arr[i];
    };

    return fibonacci(n);
};
复制代码

看一下优化后的递归树是怎么样的:

image.png

优化效果非常明显。

再以 8 举例。既然我们可以从 8 开始自顶向下递归到 0,那么是不是也可以直接就从 0 开始,自底向上计算到 8 ? 实现的代码如下所示:

var fib = function(n) {
    let arr = new Array(n+1); // 不要忘了 0
    arr[0] = 0;
    arr[1] = 1;
    
    for (let i = 2; i <= n; i++) {
        arr[i] = arr[i-1] + arr[i-2];
    }

    return arr[n];
};
复制代码

学会了吗?可以在 LeetCode 上试试看能不能通过这道题。

看到这的时候,你就已经掌握了最基本的动态规划算法了。所谓动态规划,其实就是通过记住已经求出的解,并在当前解的基础之上求解下一步的思想。

我们刚刚用一个数组,记录了已经求得的解,这个数组叫做 DP table,也叫「备忘录」。通过arr[i] = arr[i-1] + arr[i-2]不断求得下一个解,这就是在当前解的基础上求解下一步的方法。

关于这道题,还有更进一步的优化空间。可以想一下怎么样能对空间做进一步的优化?

相关代码如下所示:

var fib = function(n) {
    if (n < 2) return n;

    let a = 0, b = 0, c = 1;
    for (let i = 2; i <= n; i++) {
        a = b;
        b = c;
        c = a+b;
    }

    return c;
};
复制代码

看懂了?再接着试试这道题

如果文章给你带来了思考,给 LvLin 点个赞呗~

分类:
前端
收藏成功!
已添加到「」, 点击更改