题目地址
https://leetcode.cn/problems/climbing-stairs/
思路1:递归
递归的关键是找到递归公式和递归终止条件。假设f(n)为爬到n阶楼顶所需的方法数,又限制每次可以爬1或者2个台阶,n阶只能是从n-1阶或者n-2阶跳上去的,假设f(n-1)和f(n-2)已知,那么就有f(n)=f(n-1)+f(n-2),这就是递归公式。再考察递归终止条件,即一些简单的基本情况,很容易可以以得出:f(1)=1,f(2)=2。总结一下:
- 递归公式:f(n)=f(n-1)+f(n-2)。
- 递归终止条件:f(1)=1,f(2)=2。
将上述公式转换为代码:
class Solution {
public int climbStairs(int n) {
if(n == 1 || n == 2) {
return n;
}
return climbStairs(n-1)+climbStairs(n-2);
}
}
提交后显示计算超时:
这也是递归解法经常出现的一个问题,用一张图来看下问题出在哪里(假设n=6):
可以看到, f(3)、f(4)的值均出现了重复计算。而且随着n的增加,重复计算的节点数会变得更多,会大大增加计算时间,这也就是出现运行超时的原因。这里递归的时间复杂度大约是。
思路2:迭代
既然递归方法会出现重复计算,那么我们尝试使用迭代法避免这个重复计算的问题。与递归自上而下逐层返回相反,迭代法采用自下而上的方式计算,计算方式还是:f(n)=f(n-1)+f(n-2)。代码如下:
class Solution {
public int climbStairs(int n) {
if(n == 1 || n == 2) {
return n;
}
int cur = 2;
int prev = 1;
for(int i=3;i<=n;i++) {
cur = cur + prev;
prev = cur - prev;
}
return cur;
}
}
提交结果:
还是以n=6为例分析迭代法的执行过程:
i | cur | prev |
2 | 2 | 1 |
3 | 3 | 2 |
4 | 5 | 3 |
5 | 8 | 5 |
6 | 13 | 8 |
可以看到每次循环都会计算当前i层的方法数,且在下次循环中作为prev直接参与计算,无需再次计算,从而避免了重复计算的问题。迭代运算包含一层循环,时间复杂度是。
思路3:空间换时间
递归中出现的重复计算问题,还有一种空间换时间的解法思路,具体来讲:新增一个Hash表用于存储每个节点的结果。在自上而下的递归过程中,每遇到一个新的节点,先不着急往下递归,而是做一个判断,该节点是否已经存在于Hash表中,如果存在直接获取结果;否则计算结果并保存在Hash表中。代码如下:
class Solution {
HashMap<Integer, Integer> result = new HashMap<Integer, Integer>();
public int climbStairs(int n) {
if(n==1 || n==2) {
return n;
}
if(null != result.get(n)) {
return result.get(n);
}else{
int cur = climbStairs(n-1) + climbStairs(n-2);
result.put(n, cur);
return cur;
}
}
}
提交结果:
这里的时间复杂度同样是,但是引入了Hashmap存储中间结果,因此有
的空间复杂度。