每日一题(3-爬楼梯)

141 阅读1分钟

爬楼梯

假设你正在爬楼梯。需要 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];
    }
};