力扣解题-70. 爬楼梯

5 阅读7分钟

力扣解题-70. 爬楼梯

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

Related Topics

记忆化、数学、动态规划


第一次解答

解题思路

核心方法:动态规划空间优化版(滚动变量法),基于“斐波那契数列”的递推规律,通过两个滚动变量记录前两步的结果,避免创建dp数组,将空间复杂度从O(n)降至O(1),时间复杂度保持O(n),是本题的高效解法。

核心逻辑拆解

爬楼梯问题的核心是递推规律:

  1. 递推公式推导
    • 要爬到第n阶台阶,最后一步只能是:
      • 从第n-1阶爬1阶上来(有f(n-1)种方法);
      • 从第n-2阶爬2阶上来(有f(n-2)种方法);
    • 因此总方法数:f(n) = f(n-1) + f(n-2)
  2. 初始条件
    • f(0)=1:爬0阶(原地不动)有1种方法(边界条件,用于推导f(2));
    • f(1)=1:爬1阶只有1种方法(直接爬1阶);
  3. 滚动变量优化:无需存储所有f(0)f(n)的值,仅用两个变量记录f(n-2)f(n-1),迭代计算f(n)
具体执行逻辑
  1. 边界处理:若n<=1,直接返回1(f(0)=1f(1)=1);
  2. 初始化滚动变量
    • prev0 = 1:代表f(n-2)(初始为f(0));
    • prev1 = 1:代表f(n-1)(初始为f(1));
    • current = 0:存储当前计算的f(i)
  3. 迭代计算(从i=2i=n):
    • current = prev1 + prev0:计算f(i) = f(i-1) + f(i-2)
    • prev0 = prev1:更新f(n-2)为原f(n-1)
    • prev1 = current:更新f(n-1)为当前计算的f(i)
  4. 返回结果:迭代结束后,current即为f(n)
执行流程可视化(以n=3为例)
迭代iprev0(f(i-2))prev1(f(i-1))current(f(i))变量更新后
21(f(0))1(f(1))2(f(2))prev0=1、prev1=2
31(f(1))2(f(2))3(f(3))prev0=2、prev1=3
结束--3返回3
关键细节说明
  • 初始条件的合理性f(0)=1是数学上的边界补充,目的是让f(2)=f(1)+f(0)=2符合实际(爬2阶有2种方法);
  • 循环范围:从i=2开始到i=n,覆盖所有需要递推的阶数;
  • 变量更新顺序:先计算current,再更新prev0prev1,避免覆盖未使用的旧值;
  • 性能适配:n≤45时,int类型足够存储结果(f(45)=1836311903,未超出int范围)。
性能说明
  • 时间复杂度:O(n)(仅需一次线性迭代);
  • 空间复杂度:O(1)(仅使用3个变量,无额外数组);
  • 优势:
    1. 空间效率最优,避免了dp数组的内存开销;
    2. 代码简洁,迭代逻辑清晰,无递归栈开销;
    3. 时间效率高,线性迭代无冗余计算。
public int climbStairs(int n) {
    if(n<=1){
        return 1;
    }
    int prev0=1;//f(0)->f(n-2)
    int prev1=1;//f(1)->f(n-1)
    int current=0;
    //重要的公式是 f(n)=f(n-1)+f(n-2)
    for (int i = 2; i <= n; i++) {
        current=prev1+prev0;//相当于f(2)=f(1)+f(0)
        prev0=prev1;//更新值 f(n-2)
        prev1=current;//更新值 f(n-1)
    }
    return current;
}

示例解答

解题思路

解法1:标准动态规划法(直观版,O(n)时间+O(n)空间)

核心方法:创建dp数组存储每一步的结果,直接按递推公式填充数组,逻辑更直观,适合理解动态规划的核心思想。

代码实现
public int climbStairs(int n) {
    if (n <= 1) {
        return 1;
    }
    // dp[i] 表示爬i阶台阶的方法数
    int[] dp = new int[n + 1];
    // 初始条件
    dp[0] = 1;
    dp[1] = 1;
    // 递推填充数组
    for (int i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}
核心逻辑说明
  1. dp数组定义dp[i]表示爬i阶台阶的方法数;
  2. 初始条件dp[0]=1dp[1]=1,与滚动变量法一致;
  3. 递推填充:从i=2i=n,按dp[i] = dp[i-1] + dp[i-2]填充数组;
  4. 结果返回dp[n]即为最终答案。
性能说明
  • 时间复杂度:O(n)(与滚动变量法一致);
  • 空间复杂度:O(n)(需要存储n+1个元素的数组);
  • 优势:
    1. 逻辑直观,完全贴合动态规划“状态定义-初始条件-状态转移”的经典范式;
    2. 便于调试,可查看每一步的dp值;
  • 劣势:空间复杂度高于滚动变量法,n较大时(如n=1e5)内存开销更大。
解法2:递归+记忆化(Top-Down,O(n)时间+O(n)空间)

核心方法:通过递归实现递推公式,并用记忆化缓存避免重复计算,将纯递归的O(2ⁿ)时间复杂度优化至O(n)。

代码实现
public int climbStairs(int n) {
    // 记忆化缓存,存储已计算的f(i)
    int[] memo = new int[n + 1];
    return dfs(n, memo);
}

private int dfs(int n, int[] memo) {
    // 边界条件
    if (n <= 1) {
        return 1;
    }
    // 已计算过,直接返回缓存值
    if (memo[n] != 0) {
        return memo[n];
    }
    // 递归计算并缓存结果
    memo[n] = dfs(n - 1, memo) + dfs(n - 2, memo);
    return memo[n];
}
核心逻辑说明
  1. 记忆化缓存memo数组存储已计算的f(n),避免重复递归计算(如计算f(5)时无需重复计算f(3));
  2. 递归终止条件n<=1时返回1;
  3. 递归递推dfs(n) = dfs(n-1) + dfs(n-2),计算后存入memo
  4. 结果返回:递归结束后返回memo[n]
性能说明
  • 时间复杂度:O(n)(每个f(i)仅计算一次);
  • 空间复杂度:O(n)(递归栈深度+memo数组);
  • 优势:
    1. 符合“自顶向下”的解题思路,易于理解递推关系;
    2. 避免了纯递归的指数级时间复杂度;
  • 劣势:
    1. 递归栈有额外开销,n=45时栈深度为45,无溢出风险但效率略低;
    2. 空间复杂度高于滚动变量法。
解法3:斐波那契公式法(最优时间,O(logn))

核心方法:利用斐波那契数列的通项公式(比内公式),通过数学计算直接得到结果,时间复杂度优化至O(logn)(幂运算可通过快速幂实现)。

代码实现
public int climbStairs(int n) {
    double sqrt5 = Math.sqrt(5);
    // 斐波那契通项公式(比内公式):F(n) = (φⁿ⁺¹ - ψⁿ⁺¹)/√5,其中φ=(1+√5)/2,ψ=(1-√5)/2
    double phi = (1 + sqrt5) / 2;
    double psi = (1 - sqrt5) / 2;
    // 本题中f(n)对应斐波那契数列的F(n+1)
    double result = (Math.pow(phi, n + 1) - Math.pow(psi, n + 1)) / sqrt5;
    return (int) Math.round(result);
}
核心逻辑说明
  1. 数学推导:爬楼梯的f(n)等价于斐波那契数列的第n+1项(F(1)=1, F(2)=1, F(3)=2, F(4)=3...);
  2. 通项公式:利用比内公式直接计算斐波那契数,避免迭代/递归;
  3. 精度处理:通过Math.round四舍五入,避免浮点数精度误差。
性能说明
  • 时间复杂度:O(logn)(Math.pow的底层实现为快速幂,时间复杂度O(logn));
  • 空间复杂度:O(1)(仅使用常数级变量);
  • 优势:
    1. 时间复杂度最优,n越大优势越明显;
    2. 无循环/递归,代码极简;
  • 劣势:
    1. 依赖数学公式,不易理解和推导;
    2. 浮点数运算存在精度风险(n=45时结果正确,但n极大时可能误差);
    3. 工程中可读性差,不如迭代法直观。

总结

  1. 滚动变量动态规划(第一次解答):O(n)时间+O(1)空间,空间最优,工程实践首选;
  2. 标准动态规划法:O(n)时间+O(n)空间,逻辑直观,适合理解动态规划思想;
  3. 递归+记忆化:O(n)时间+O(n)空间,自顶向下解题,易于理解递推关系;
  4. 斐波那契公式法:O(logn)时间+O(1)空间,数学最优解,适合追求极致效率的场景;
  5. 关键技巧
    • 核心思想:爬楼梯问题本质是斐波那契数列,核心递推公式为f(n)=f(n-1)+f(n-2)
    • 优化方向:从标准DP(O(n)空间)→ 滚动变量(O(1)空间)→ 公式法(O(logn)时间);
    • 场景选择:日常开发选滚动变量法,教学选标准DP/记忆化递归,极致效率选公式法。