由于昨晚加班的比较晚,回来的时候已经不够时间学习了,所以昨天停了。今天来一道面试问的比较多的题:leetcode-剑指 Offer 10- II. 青蛙跳台阶问题,这题本身是一道简单题,但其可以引申出很多类似的问题,比如:限定相近两步不能相同的步数?最少跳的步数是多少?不过今天不深究到那地步,恕在下实力不足。今天先讨论原题:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
示例 3:
输入:n = 0
输出:1
提示: 0 <= n <= 100
这道题刚入手的时候会有点不知所措,因为找不到规律。其实可以动手简单地计算下,当n=1;n=2;n=3,得到的结果是多少。当n=1时,只能跳一步;当n=2时,可以一次跳两步,也可以跳两次一步;然后当n=3时,可以跳2+1,也可以跳1+1+1,也可以跳1+2,有3种跳法....以此类推,不难发现当前项的步数是前两项的步数之和。
在数学上,这种叫斐波那契数列,第n项等于第n-1和第n-2项之和。那么转成代码,则是:
var numWays = function(n) {
if (n <= 1) return 1
return numWays(n - 1) % 1000000007 + numWays(n - 2) % 1000000007
};
不过这种解法的时间复杂度是非常高的,为O(2^n),因为中间做了很多重复的运算,比如climbStairs(n - 1) = climbStairs(n - 2) + climbStairs(n - 3)。其实可以使用一个数组把计算过的值保存起来,当后面用到就可以直接使用。
var numWays = function(n) {
let arr = [0, 1, 2, 3, 5]
if (arr[n]) {
return arr[n]
} else {
arr[n] = numWays(n - 1) % 1000000007 + numWays(n - 2) % 1000000007
return arr[n]
}
};
优化完毕,这种解法的时间复杂度仅为O(n),但需要理解会递归的思想。还有第三种解法:动态规则,时间复杂度也是为O(n),不过思路和上面那种相反。上面的是从上自下,而这种解法是自下往上。
var numWays = function(n) {
let l = 0, r = 1
while (n > 0) {
let res = l % 1000000007 + r % 1000000007;
l = r;
r = res;
n--;
}
return r
};
反而第三种解法会比较好理解一点,从第0项开进行相加,一直加到第n项。即可得到结果。
还有第四种解法,利用斐波那契数列的通项公式,可以再研究研究下。其实在这一题中只有第三种解法是不会超时,开始的两种解法都会出现超时。因为n最大可以到100。通用公式解法不会超时,但因为计算出来的数过大会丢失精准度,并且取不了模。今天内容就到这。