背景
这道题之前刷过,记得是一道非常基础的动态规划题,但怎么也没有想出来该怎么做这道题。
题目
假设你正在爬楼梯。需要 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 阶
来源:力扣(LeetCode)
思路
我们可以从最后一步往前推:现在我们在第n阶,那么我们要到达第n阶,应该怎么做呢?
最后一步走一步,或者两步。那么爬上n阶楼梯的方法数=爬上n-1阶楼梯的方法数(最后爬一步) + 爬上n-2阶楼梯的方法数(最后爬两步)
解决方法一:递归
class Solution {
public int climbStairs(int n) {
return doClimb(0, n);
}
private int doClimb(int i, int n) {
if (i == n)
return 1;
if (i == n - 1)
return 1;
return doClimb(i + 1, n) \+ doClimb(i + 2, n);
}
}
这种方式在leetcode上跑的时候会超时,时间复制读为O(2^n)。
解决方法二:带备忘录的递归算法
在上面一种算法中,随着n的增大,计算量会急剧膨胀,通过leetcode的观察会发现在n=44时超时。
在下面的算法当中,我们增加一个Map结构用来存储曾经计算过的结果,下次在计算相同的内容时,我们先查询Map中是否已经计算过了,如果计算过了就直接把结果取出来用。
import java.util.HashMap;
class Solution {
// 备忘录记录从i阶到n阶有多少种方法
private HashMap<Integer, Integer> memento = new HashMap<>();
public int climbStairs(int n) {
return doClimb(0, n);
}
private int doClimb(int i, int n) {
if (i == n || i == n - 1)
return 1;
int marchWithOneStep;
if (!memento.containsKey(i + 1)) {
marchWithOneStep = doClimb(i + 1, n);
memento.put(i + 1, marchWithOneStep);
} else {
marchWithOneStep = memento.get(i + 1);
}
int marchWithTwoStep;
if (!memento.containsKey(i + 2)) {
marchWithTwoStep = doClimb(i + 2, n);
memento.put(i + 2, n);
} else {
marchWithTwoStep = memento.get(i + 2);
}
return marchWithOneStep + marchWithTwoStep;
}
}
解决方法三:动态规划
动态规划是把计算过程中变量的值作为一种状态来看的算法,最终要计算的结果是由前面计算出来的中间状态计算出来的。
在该题中,从0阶爬上n阶的方法 = 从0阶爬到n-1阶 + 从0阶爬到n-2阶。
class Solution {
public int climbStairs(int n) {
if (n == 1)
return 1;
int[] nums = new int[n];
nums[0] = 1;
nums[1] = 2;
for (int i = 2; i < nums.length; i++) {
nums[i] = nums[i - 1] + nums[i - 2];
}
return nums[n - 1];
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。