假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 注意:给定 n 是一个正整数。

示例1: 输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

示例2: 输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

使用动态规划解题

状态转移方程:

dp(n) = dp(n-1) + dp(n-2)

Base case:

当 n = 0 时,只有1种走法:dp(0) = 1

当 n = 1 时,同样也只有1种走法:dp(1) = 1

此时内心OS:这特喵的不就是斐波那契数列么,喂喂喂,禁止套娃啊

代码如下:

  1. public int climbStairs(int n){
  2. if (n == 0) return 1;
  3. if (n == 1) return 1;
  4. return climbStairs(n - 2) + climbStairs(n - 1);
  5. }

优化

上面的代码在 LeetCode上直接运行会报超时而无法通过测试用例。

解决重叠子问题

这里使用dp table解决重叠子问题:

  1. public int solution(int n) {
  2. int[] dp = new int[n + 1];
  3. dp[0] = 1;
  4. dp[1] = 1;
  5. for (int i = 2; i <= n; i++) {
  6. dp[i] = dp[i - 1] + dp[i - 2];
  7. }
  8. return dp[n];
  9. }

状态压缩

通过状态转移方程我们可以很明确的看到,对于状态 dp(n)而言,它的值只与 dp(n-1)dp(n-2)有关。因此,我们可以使用滚动数组压缩状态:

  1. public int climbStairs(int n) {
  2. int p = 0, q = 0, r = 1;
  3. for (int i = 1; i <= n; ++i) {
  4. p = q;
  5. q = r;
  6. r = p + q;
  7. }
  8. return r;
  9. }

效率

  • 经过优化后的空间复杂度:O(1)
  • 经过优化后的时间复杂度:O(n)