爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
相关标签
链接:leetcode-cn.com/leetbook/re…
解题思路
首先明确爬1阶楼梯为一种可能,可以假设我们要爬x阶楼梯,那当爬到最后一步的时候,可能爬了两阶或者是一阶,所以爬x阶楼梯的情况为爬x-1阶楼梯加上爬x-2阶楼梯的数量,也就是:
f(x)=f(x−1)+f(x−2)
具体实现一个简单的尾递归很容易
int climbStairs(int n) {
if(n<2)return 1;
return climbStairs(n-1)+climbStairs(n-2);
}
其实这就是斐波那契数列,随着n是呈指数增长的,越来越逼近(1+0.618..黄金分割比例)^n。当我们递归层数多的时候有很可能会超时,当我们计算f(n-1)与f(n-2)的时候,其实重复计算了一次f(n-2),以此类推,时间复杂度是O(2^n),可以用利用一个map存起来避免重复计算,使时间复杂度达到O(n),空间复杂度O(n)
class Solution {
public:
unordered_map<int,int> mp;
int climbStairs(int n) {
if(n<2)return 1;
if(mp.find(n)==mp.end()){
mp[n]=climbStairs(n-1)+climbStairs(n-2);
}
return mp[n];
}
};
当然也可以反过来先计算子问题,一步步推出f(n),例如f(1),f(2),f(3)....f(n-1),f(n)这样做
class Solution {
public:
int climbStairs(int n) {
int f[46]={1,1};
for(int i=2;i<=n;i++){
f[i]=f[i-1]+f[i-2];
}
return f[n];
}
};
这样也是O(n)时间,O(n)空间,和尾递归版效率是差不多的,那样也没有还能优化的地方呢?
观察一下不难发现我们其实只需要记录f(i-1)与f(i-2)就可以推出i来了,当我们求出f(i)来了,可以将值存到f(i-2)的位置,因为求后续的f(i+1)并不需要f(i-2),这样可以做到O(1)的空间复杂度了。代码如下:
class Solution {
public:
int climbStairs(int n) {
int f[]={1,1};
for(int i=2;i<=n;i++){
f[i%2]=f[(i-1)%2]+f[i%2];//利用取模将f[i]的值放到f[i-2]的位置
}
return f[n%2];
}
};