问题

假设你正在爬楼梯。需要 阶你才能到达楼顶。

每次你可以爬 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 是一个正整数。

示例 1:

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

  1. 阶 +

示例 2:

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

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

解答1

此题目可以说是最经典(另一种说法是最“容易”)的动态规划的题目。 动态规划是将一个大问题分解为多个简单的子问题来求解的方法。 动态规划常常适用于有重叠子问题与最优子结构的问题。

思路

  1. 为总的方法数量
  2. 可以看到,到第阶的方法其实一共有两种方式
    • 从第阶爬一步到达第
    • 从第阶爬两步到达第
  3. 因此可以总结出公式
\begin{cases} 1, & \text{$n = 1$} \\ 2,& \text{$n = 2$} \\ f(n-1) +f(n-2), & \text{$n>2$} \end{cases}$$可以发现,这个就是斐波那契数列的变种。 #代码1 ## java实现 ```java class Solution { public int climbStairs(int n) { return resolve(n); } public int resolve(int n) { if (n==1) { return 1; } if (n==2) { return 2; } return resolve(n-1)+resolve(n-2); } } ``` 代码非常的简洁,马上丢到leetcode上跑一次,居然执行了10801 ms。击败了0.98%的人。。。 # 解法2 解法1虽然勉强能跑,但是有一个非常严重的问题是,重复计算量过大。 $f(n)$与$f(n-1)$均用到了$f(n-2)$但是因为并没有暂存的地方,导致$f(n-2)$被计算了两次。 比如计算$f(5)$: ```dot strict graph { a -- b a -- b b -- a [color=blue] } ``` 此时考虑到以往的数据会被重复使用,那么何不做一个数组暂存数据呢。 # 代码2 ## java实现 ```java class Solution { public int climbStairs(int n) { int[] cache = new int[n+1]; return resolve(n); } public int resolve(int n, int[] cache) { if (n==1) { return 1; } if (n==2) { return 2; } if (cache[n] ==0) { cache[n] = resolve(n-1,cache) +resolve(n-2,cache); } return cache[n]; } } ``` 此方法4ms执行完成。 # 解法3 上述两种方法,均是递归调用,如何转为迭代呢? # 代码3 ## java实现 ```java class Solution { public int climbStairs(int n) { int[] cache = new int[n+1]; cache[0] = cache[1] = 1; for(int i=2;i<=n;i++) { cache[i] = cache[i-1] + cache[i-2]; } return cache(n); } } ```