力扣解题-70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
提示: 1 <= n <= 45
Related Topics
记忆化、数学、动态规划
第一次解答
解题思路
核心方法:动态规划空间优化版(滚动变量法),基于“斐波那契数列”的递推规律,通过两个滚动变量记录前两步的结果,避免创建dp数组,将空间复杂度从O(n)降至O(1),时间复杂度保持O(n),是本题的高效解法。
核心逻辑拆解
爬楼梯问题的核心是递推规律:
- 递推公式推导:
- 要爬到第
n阶台阶,最后一步只能是:- 从第
n-1阶爬1阶上来(有f(n-1)种方法); - 从第
n-2阶爬2阶上来(有f(n-2)种方法);
- 从第
- 因此总方法数:
f(n) = f(n-1) + f(n-2);
- 要爬到第
- 初始条件:
f(0)=1:爬0阶(原地不动)有1种方法(边界条件,用于推导f(2));f(1)=1:爬1阶只有1种方法(直接爬1阶);
- 滚动变量优化:无需存储所有
f(0)到f(n)的值,仅用两个变量记录f(n-2)和f(n-1),迭代计算f(n)。
具体执行逻辑
- 边界处理:若
n<=1,直接返回1(f(0)=1、f(1)=1); - 初始化滚动变量:
prev0 = 1:代表f(n-2)(初始为f(0));prev1 = 1:代表f(n-1)(初始为f(1));current = 0:存储当前计算的f(i);
- 迭代计算(从
i=2到i=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);
- 返回结果:迭代结束后,
current即为f(n)。
执行流程可视化(以n=3为例)
| 迭代i | prev0(f(i-2)) | prev1(f(i-1)) | current(f(i)) | 变量更新后 |
|---|---|---|---|---|
| 2 | 1(f(0)) | 1(f(1)) | 2(f(2)) | prev0=1、prev1=2 |
| 3 | 1(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,再更新prev0和prev1,避免覆盖未使用的旧值; - 性能适配:n≤45时,int类型足够存储结果(
f(45)=1836311903,未超出int范围)。
性能说明
- 时间复杂度:O(n)(仅需一次线性迭代);
- 空间复杂度:O(1)(仅使用3个变量,无额外数组);
- 优势:
- 空间效率最优,避免了dp数组的内存开销;
- 代码简洁,迭代逻辑清晰,无递归栈开销;
- 时间效率高,线性迭代无冗余计算。
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];
}
核心逻辑说明
- dp数组定义:
dp[i]表示爬i阶台阶的方法数; - 初始条件:
dp[0]=1、dp[1]=1,与滚动变量法一致; - 递推填充:从
i=2到i=n,按dp[i] = dp[i-1] + dp[i-2]填充数组; - 结果返回:
dp[n]即为最终答案。
性能说明
- 时间复杂度:O(n)(与滚动变量法一致);
- 空间复杂度:O(n)(需要存储n+1个元素的数组);
- 优势:
- 逻辑直观,完全贴合动态规划“状态定义-初始条件-状态转移”的经典范式;
- 便于调试,可查看每一步的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];
}
核心逻辑说明
- 记忆化缓存:
memo数组存储已计算的f(n),避免重复递归计算(如计算f(5)时无需重复计算f(3)); - 递归终止条件:
n<=1时返回1; - 递归递推:
dfs(n) = dfs(n-1) + dfs(n-2),计算后存入memo; - 结果返回:递归结束后返回
memo[n]。
性能说明
- 时间复杂度:O(n)(每个
f(i)仅计算一次); - 空间复杂度:O(n)(递归栈深度+memo数组);
- 优势:
- 符合“自顶向下”的解题思路,易于理解递推关系;
- 避免了纯递归的指数级时间复杂度;
- 劣势:
- 递归栈有额外开销,n=45时栈深度为45,无溢出风险但效率略低;
- 空间复杂度高于滚动变量法。
解法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);
}
核心逻辑说明
- 数学推导:爬楼梯的
f(n)等价于斐波那契数列的第n+1项(F(1)=1, F(2)=1, F(3)=2, F(4)=3...); - 通项公式:利用比内公式直接计算斐波那契数,避免迭代/递归;
- 精度处理:通过
Math.round四舍五入,避免浮点数精度误差。
性能说明
- 时间复杂度:O(logn)(Math.pow的底层实现为快速幂,时间复杂度O(logn));
- 空间复杂度:O(1)(仅使用常数级变量);
- 优势:
- 时间复杂度最优,n越大优势越明显;
- 无循环/递归,代码极简;
- 劣势:
- 依赖数学公式,不易理解和推导;
- 浮点数运算存在精度风险(n=45时结果正确,但n极大时可能误差);
- 工程中可读性差,不如迭代法直观。
总结
- 滚动变量动态规划(第一次解答):O(n)时间+O(1)空间,空间最优,工程实践首选;
- 标准动态规划法:O(n)时间+O(n)空间,逻辑直观,适合理解动态规划思想;
- 递归+记忆化:O(n)时间+O(n)空间,自顶向下解题,易于理解递推关系;
- 斐波那契公式法:O(logn)时间+O(1)空间,数学最优解,适合追求极致效率的场景;
- 关键技巧:
- 核心思想:爬楼梯问题本质是斐波那契数列,核心递推公式为
f(n)=f(n-1)+f(n-2); - 优化方向:从标准DP(O(n)空间)→ 滚动变量(O(1)空间)→ 公式法(O(logn)时间);
- 场景选择:日常开发选滚动变量法,教学选标准DP/记忆化递归,极致效率选公式法。
- 核心思想:爬楼梯问题本质是斐波那契数列,核心递推公式为