青蛙跳台阶问题

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

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

爬楼梯是一样的题目

示例 1:

输入:n = 2
输出:2

示例 2:

输入:n = 7
输出:21

示例 3:

输入:n = 0
输出:1

提示:0 <= n <= 100

1
2
3
4
5
class Solution {
public int numWays(int n) {

}
}

方法一:动态规划

此类求 多少种可能性 的题目一般都有 递推性质 ,即 f(n) 和 f(n-1)…f(1) 之间是有联系的。

设跳上 n 级台阶有 f(n) 种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1 级或 2 级台阶。

  1. 当为 1 级台阶: 剩 n-1 个台阶,此情况共有 f(n-1) 种跳法;

  2. 当为 2 级台阶: 剩 n-2 个台阶,此情况共有 f(n-2) 种跳法。

f(n) 为以上两种情况之和,即 f(n)=f(n-1)+f(n-2) ,以上递推性质为斐波那契数列。本题可转化为 求斐波那契数列第 n 项的值 ,与 斐波那契数列 等价,唯一的不同在于起始数字不同。

  • 青蛙跳台阶问题: f(0)=1,f(1)=1,f(2)=2;
  • 斐波那契数列问题: f(0)=0,f(1)=1,f(2)= 1
Picture13.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 原动态规划 空间复杂度为 O(n)
class Solution {
public int numWays(int n) {
if(n <= 1) return 1;
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
dp[i] %= 1000000007;
}
return dp[n];
}
}

// 优化版
class Solution {
public int numWays(int n) {
if(n == 0) return 1; // 青蛙爬台阶,示例有 0 = 1
if(n <= 2) return n;
int a = 1, b = 2, sum = 0;
for(int i = 3; i <= n; i++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return b;
}
}
var numWays = function(n) {
if(n === 0) return 1
if(n <= 2) return n
let a = 1, b = 2, sum = 0
for(let i = 3; i <= n; i++) {
sum = (a + b) % 1000000007
a = b
b = sum
}
return b
};

复杂度分析:

  • 时间复杂度:O(N),计算 f(n) 需循环 n 次,每轮循环内计算操作使用 O(1)
  • 空间复杂度:O(1),几个标志变量使用常数大小的额外空间

执行结果:通过

  • 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

  • 内存消耗:36.2 MB, 在所有 Java 提交中击败了81.08%的用户

变形:不能跳到7与7的倍数的台阶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// class Solution {
// public int numWays(int n) {
// // if(n == 0) return 1; // 青蛙爬台阶,示例有 0 = 1
// if(n <= 2) return n;
// int a = 1, b = 2, sum = 0;
// for(int i = 3; i <= n; i++) {
// if(i % 7 == 0) {
// continue;
// } else if((i - 1) % 7 == 0) {
// b = 0;
// } else if((i - 2) %7 == 0) {
// a = 0;
// }
// sum = (a + b) % 1000000007;
// a = b;
// b = sum;
// }
// return b;
// }
// }

变形:不能连续跳两个台阶

题目里“不能连续跳两个台阶”,即一旦跳了两个台阶,则下一步只能跳一步(2->1),把它们连在一起就一共跳了三个台阶,所以问题可以转化为要么跳一个台阶,要么跳三个台阶(当然还是有点区别的,只不过因为结果是一样的,所以可以这么转化),于是就有了 f(x) = f(x-1) + f(x-3)

1
2
3
4
5
6
7
8
9
10
11
12
public int climbStarts2(int n) {
if(n <= 1) return 1; // 防止数组溢出(f[2])
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-3];
dp[i] %= 1000000007;
}
return dp[n];
}