解题思路 方法一:动态规划 思路和算法

我们用 f(x) 表示爬到第 xx 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:

f(x) = f(x - 1) + f(x - 2) f(x)=f(x−1)+f(x−2)

它意味着爬到第 x 级台阶的方案数是爬到第 x−1 级台阶的方案数和爬到第 x - 2x−2 级台阶的方案数的和。很好理解,因为每次只能爬 1 级或 2 级,所以 f(x) 只能从 f(x−1) 和 f(x−2) 转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。

以上是动态规划的转移方程,下面我们来讨论边界条件。我们是从第 0 级开始爬的,所以从第 0 级爬到第 0 级我们可以看作只有一种方案,即 f(0)=1;从第 0 级到第 1 级也只有一种方案,即爬一级,f(1)=1。这两个作为边界条件就可以继续向后推导出第 n 级的正确结果。我们不妨写几项来验证一下,根据转移方程得到 f(2)=2,f(3)=3,f(4)=5,……,我们把这些情况都枚举出来,发现计算的结果是正确的。

我们不难通过转移方程和边界条件给出一个时间复杂度和空间复杂度都是 O(n) 的实现,但是由于这里的 f(x) 只和 f(x−1) 与 f(x−2) 有关,所以我们可以用「滚动数组思想」把空间复杂度优化成O(1)。下面的代码中给出的就是这种实现。 https://assets.leetcode-cn.com/solution-static/70/70_fig1.gif

总结 这里形成的数列正好是斐波那契数列,答案要求的 f(n) 即是斐波那契数列的第 n 项(下标从 0 开始)。我们来总结一下斐波那契数列第 n 项的求解方法:

nn 比较小的时候,可以直接使用过递归法求解,不做任何记忆化操作,时间复杂度是 O(2^n),存在很多冗余计算。 一般情况下,我们使用「记忆化搜索」或者「迭代」的方法,实现这个转移方程,时间复杂度和空间复杂度都可以做到 O(n)。 为了优化空间复杂度,我们可以不用保存 f(x−2) 之前的项,我们只用三个变量来维护 f(x)、f(x−1) 和 f(x−2),你可以理解成是把「滚动数组思想」应用在了动态规划中,也可以理解成是一种递推,这样把空间复杂度优化到了 O(1)。 随着 n 的不断增大 O(n) 可能已经不能满足我们的需要了,我们可以用「矩阵快速幂」的方法把算法加速到 O(logn)。 我们也可以把 n 代入斐波那契数列的通项公式计算结果,但是如果我们用浮点数计算来实现,可能会产生精度误差。

代码

class Solution {
    
    public int climbStairs(int n) {

        //斐波那契方法 时间复杂度复杂度过高 O(2的n次方) 空间复杂度为O(n)
        //n 比较小的时候,可以直接使用过递归法求解,不做任何记忆化操作,时间复杂度是 O(2^n),存在很多冗余计算。
        // if (n == 0) {
        //     return 1;
        // }
 
        // if (n == 1) {
        //     return 1;
        // }
 
        // return climbStairs(n-1) + climbStairs(n-2);

        //我们不难通过转移方程和边界条件给出一个时间复杂度和空间复杂度都是 O(n)O(n) 的实现,
        //但是由于这里的 f(x)f(x) 只和 f(x - 1)f(x−1) 与 f(x - 2)f(x−2) 有关,所以我们可以用「滚动数组思想」把空间复杂度优化成 O(1)O(1)。

        int p = 0;
        int q = 0;
        int r = 1;

        //滚动数组思想
        for (int i = 1; i <= n; i++) {
            p = q;
            q = r;
            r = p + q;
        }

        return r;

方法2:斐波那契不建议

public class Solution {
    
     /**
     * 斐波那契(指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,
     * 斐波那契数列以如下被以递推的方法定义:
     * F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
     * 对于第n个台阶来说,只能从n-1或者n-2的台阶跳上来,所以
     * F(n) = F(n-1) + F(n-2)
     * 斐波拉契数序列,初始条件
     * n=1:只能一种方法
     * n=2:两种(和斐波那契数列稍微有些不同,0是台阶的起点,0、1、1、2、3、5、8、13、21、34、……
     * 当n=2的时候 可以直接从0跳到1 再从1跳到2 或者 从0直接跳到2)
     * 递归一下就好了
     */
    public int jumpFloor(int target) {
       
        if (target == 0) {
            return 1;
        }
        
        if (target == 1) {
            return 1;
        }
        
        return jumpFloor(target-1) + jumpFloor(target-2);
    }
}