剑指Offer系列算法-JavaScript解法-1|刷题打卡

95 阅读3分钟

第一天打卡,先给大家来道简单的经典题目开开胃

一、先看题

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例1:

输入:n = 2

输出:2

示例2:

输入:n = 7

输出:21

示例3:

输入:n = 0

输出:1

  • 0 <= n <= 100

二、整理思路

这道题只要刷过题的小伙伴应该都知道,毕竟是经典之一,斐波那契数列的算法,最简单的就是递归思想获得答案。

f(n)=f(n2)+f(n1)f(n)=f(n-2)+f(n-1)

但是! 如果你敢在面试的时候只说出递归解法的话,那你可能就直接被pass,因为递归解法是这个算法中最弱的解法,时间复杂度无与伦比。当然,第一次写这个题我的小青蛙就直接累死在跳台阶的过程中了= =。

现在给出的这个算法算是初步优化之后的一个算法,比递归法好一点,属于动态规划方法,思想就是通过两个变量来存储f(n-1)和f(n-2)从而避免重复计算,减少计算量。

/**
 * 动态规划法
 * @param {number} n
 * @return {number}
 */
var numWays = function (n) {
    if (n == 0|n == 1) return 1
    if (n > 1) {
        var num1 = 1;
        var num2 = 1;
        var res;
        for (let i = 2; i <= n; i++) {
            res=(num1+num2)%1000000007;
            num1=num2;
            num2=res;
        }
        return res;
    }
};

除了常见的动态规划法,还有种更高端的方法:通项公式法!

通项公式法要用到高数的知识了!没错!就是高数,在我已经把高数忘得一干二净的现在,也只能直接贴原文分享给大家了,如果以后重新复习高数了的话,再回来看看,也许会有不一样的收获。

如果一个递归式形如 f(n)=i=1maif(ni)f(n)=\sum_{i = 1}^{m} a_i f(n - i),则f(n)f(n)是齐次线性递推,根据递推方程 f(n)=f(n1)+f(n2)f(n) = f(n - 1) + f(n - 2),我们可以写出这样的特征方程:

x2=x+1 x^2 = x + 1

求得x1=(1+5)2x_1=\frac{(1+\sqrt 5)}{2},x2=(15)2x_2=\frac{(1-\sqrt 5)}{2},设通解为f(n)=c1x1n+c2x2nf(n)=c_1x_1^n+c_2x_2^n,代入初始条件f(1)=1f(1)=1f(2)=1f(2)=1,得c1=15c_1=\frac{1}{\sqrt5},c2=15c_2=-\frac{1}{\sqrt5},我们得到了这个递推数列的通项公式:

f(n)=15[(1+5)n2(15)n2]f(n)=\frac{1}{\sqrt5}[\frac{(1+\sqrt 5)^n}{2}-\frac{(1-\sqrt 5)^n}{2}]

接着我们就可以通过这个公式直接求第 n 项了。

(看到这里不禁流下了不学无术的泪水)

/**
 * 神奇的通项公式法
 * @param {number} n
 * @return {number}
 */
 var climbStairs = function(n) {
    const sqrt5 = Math.sqrt(5);
    const fibn = Math.pow((1 + sqrt5) / 2, n + 1) - Math.pow((1 - sqrt5) / 2, n + 1);
    return Math.round(fibn / sqrt5);
};

来源:力扣(LeetCode

三、总结

拿到一个题之后不要急着写,首先应该分析一下思路,找到算法的规律,然后再根据自己所学尽量选择时间复杂度和空间复杂度更小的方式进行解答,在测试样例通过之后,也应该多看看他人思路,这样不仅有助于扩展所学,也能不断进行思想碰撞,这样才能不断进步。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情