当前位置: 首页 > news >正文

LeetCode Hot 100 - 爬楼梯完全题解

题目描述

假设你正在爬楼梯。需要n阶你才能到达楼顶。

每次你可以爬12个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

text

输入:n = 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶

示例 2:

text

输入:n = 3 输出:3 解释:有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶

示例 3:

text

输入:n = 4 输出:5 解释:5种方法: 1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2

示例 4:

text

输入:n = 1 输出:1

约束条件:

  • 1 <= n <= 45


解题思路

核心思想:这是一个经典的动态规划问题,本质上就是斐波那契数列

状态转移方程:

text

dp[i] = dp[i-1] + dp[i-2]

解释:爬到第i阶楼梯的方法数 = 爬到第i-1阶的方法数(再爬 1 阶)+ 爬到第i-2阶的方法数(再爬 2 阶)

边界条件:

text

dp[1] = 1 // 只有 1 种方法:1 阶 dp[2] = 2 // 2 种方法:1+1 或 2

方法一:递归(暴力解法)

思路

直接按照状态转移方程递归求解。

代码实现

java

public int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; return climbStairs(n - 1) + climbStairs(n - 2); }

复杂度分析

  • 时间复杂度:O(2ⁿ) — 指数级,会超时

  • 空间复杂度:O(n) — 递归栈深度

优缺点

  • ✅ 思路最直观

  • ❌ 效率极低,n=45 时无法运行


方法二:递归 + 记忆化搜索(带备忘录)

思路

使用数组存储已计算过的结果,避免重复计算。

代码实现

java

public int climbStairs(int n) { int[] memo = new int[n + 1]; return dfs(n, memo); } private int dfs(int n, int[] memo) { if (n == 1) return 1; if (n == 2) return 2; if (memo[n] != 0) return memo[n]; memo[n] = dfs(n - 1, memo) + dfs(n - 2, memo); return memo[n]; }

复杂度分析

  • 时间复杂度:O(n) — 每个 n 只计算一次

  • 空间复杂度:O(n) — 递归栈 + 备忘录数组

优缺点

  • ✅ 时间复杂度优化到 O(n)

  • ❌ 仍有递归栈开销


方法三:动态规划(DP数组)

思路

使用 DP 数组自底向上计算。

代码实现

java

public int climbStairs(int n) { if (n == 1) return 1; int[] dp = new int[n + 1]; dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; }

手动模拟

n = 5为例:

idp[i]计算过程
11边界条件
22边界条件
33dp[2] + dp[1] = 2 + 1 = 3
45dp[3] + dp[2] = 3 + 2 = 5
58dp[4] + dp[3] = 5 + 3 = 8

结果:8

复杂度分析

  • 时间复杂度:O(n) — 一次循环

  • 空间复杂度:O(n) — DP 数组

优缺点

  • ✅ 时间复杂度 O(n)

  • ✅ 思路清晰易懂

  • ❌ 空间复杂度 O(n),可以优化


方法四:动态规划 + 滚动数组(空间优化)⭐

思路

因为dp[i]只依赖前两个状态,所以只需要两个变量。

代码实现

java

public int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; int prev2 = 1; // dp[i-2] int prev1 = 2; // dp[i-1] int current = 0; for (int i = 3; i <= n; i++) { current = prev1 + prev2; prev2 = prev1; prev1 = current; } return prev1; // 或 current }

变量变化过程

n = 5为例:

iprev2prev1current说明
初始12-dp[1]=1, dp[2]=2
3123current = 2+1=3,然后滚动
4235current = 3+2=5
5358current = 5+3=8

返回 prev1 = 5?等等,这里要注意循环结束后的值!

实际上循环结束后prev1就是dp[n]

  • i=3: prev2=1, prev1=2, current=3 → 更新: prev2=2, prev1=3

  • i=4: prev2=2, prev1=3, current=5 → 更新: prev2=3, prev1=5

  • i=5: prev2=3, prev1=5, current=8 → 更新: prev2=5, prev1=8

  • 循环结束,返回 prev1=8 ✅

复杂度分析

  • 时间复杂度:O(n) — 一次循环

  • 空间复杂度:O(1) — 只使用三个变量

优缺点

  • ✅ 时间复杂度 O(n)

  • ✅ 空间复杂度 O(1)

  • ✅ 代码简洁

  • ⭐ 面试推荐写法


方法五:通项公式(数学法)

思路

爬楼梯问题本质是斐波那契数列,有闭合解。

斐波那契通项公式:

text

F(n) = (φⁿ - ψⁿ) / √5 其中 φ = (1 + √5) / 2 ≈ 1.618, ψ = (1 - √5) / 2 ≈ -0.618

但注意:爬楼梯数列是F(1)=1, F(2)=2, F(3)=3, F(4)=5...
实际上是斐波那契数列偏移:climbStairs(n) = Fib(n+1)

代码实现

java

public int climbStairs(int n) { double sqrt5 = Math.sqrt(5); double phi = (1 + sqrt5) / 2; double psi = (1 - sqrt5) / 2; // 爬楼梯(n) = Fib(n+1) = (φ^(n+1) - ψ^(n+1)) / √5 double result = (Math.pow(phi, n + 1) - Math.pow(psi, n + 1)) / sqrt5; return (int) Math.round(result); }

复杂度分析

  • 时间复杂度:O(log n) —Math.pow的时间复杂度

  • 空间复杂度:O(1)

优缺点

  • ✅ 时间复杂度最优 O(log n)

  • ✅ 空间复杂度 O(1)

  • ❌ 浮点数精度问题(n 较大时可能出错)

  • ❌ 不推荐面试使用(过于数学化)


方法六:矩阵快速幂

思路

利用矩阵乘法快速计算斐波那契数列。

矩阵公式:

text

[ F(n) F(n-1) ] = [ 1 1 ] ^ (n-2) * [ F(2) F(1) ] [ F(n-1) F(n-2) ] [ 1 0 ] [ F(1) F(0) ]

代码实现

java

public int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; int[][] base = {{1, 1}, {1, 0}}; int[][] result = matrixPower(base, n - 2); // result[0][0] * 2 + result[0][1] * 1 return result[0][0] * 2 + result[0][1] * 1; } private int[][] matrixPower(int[][] matrix, int n) { int[][] result = {{1, 0}, {0, 1}}; // 单位矩阵 int[][] base = matrix; while (n > 0) { if ((n & 1) == 1) { result = matrixMultiply(result, base); } base = matrixMultiply(base, base); n >>= 1; } return result; } private int[][] matrixMultiply(int[][] a, int[][] b) { int[][] c = new int[2][2]; c[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0]; c[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1]; c[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0]; c[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1]; return c; }

复杂度分析

  • 时间复杂度:O(log n) — 快速幂

  • 空间复杂度:O(1)

优缺点

  • ✅ 时间复杂度最优 O(log n)

  • ✅ 没有浮点数精度问题

  • ❌ 代码复杂,面试不常用


方法对比总结

方法时间复杂度空间复杂度难度推荐度
递归O(2ⁿ)O(n)简单
递归+记忆化O(n)O(n)中等⭐⭐⭐
DP数组O(n)O(n)简单⭐⭐⭐
滚动数组O(n)O(1)简单⭐⭐⭐⭐⭐
通项公式O(log n)O(1)较难⭐⭐
矩阵快速幂O(log n)O(1)⭐⭐

图文详解

爬楼梯的递归树(n=5)

text

climb(5) / \ climb(4) climb(3) / \ / \ climb(3) climb(2) climb(2) climb(1) / \ | | | climb(2) climb(1) 2 2 1 | | 2 1 结果 = 8

动态规划状态转移图

text

第1阶: 1种方法 第2阶: 2种方法 第3阶 = 第2阶 + 第1阶 = 2 + 1 = 3种 第4阶 = 第3阶 + 第2阶 = 3 + 2 = 5种 第5阶 = 第4阶 + 第3阶 = 5 + 3 = 8种 可视化: 台阶: 1 2 3 4 5 方法数: 1 → 2 → 3 → 5 → 8 └─加─┘ └─加─┘

路径枚举(n=4,共5种)

text

1. 1 + 1 + 1 + 1 2. 1 + 1 + 2 3. 1 + 2 + 1 4. 2 + 1 + 1 5. 2 + 2

常见问题 Q&A

Q1:为什么是斐波那契数列?

A:因为到达第 i 阶只有两种途径:

  • 从第 i-1 阶爬 1 阶

  • 从第 i-2 阶爬 2 阶
    所以f(i) = f(i-1) + f(i-2),这正是斐波那契递推公式。

Q2:边界条件为什么是 dp[1]=1, dp[2]=2?

A:

  • 1 阶:只能爬 1 阶 → 1 种

  • 2 阶:可以 1+1 或 2 → 2 种
    注意这和标准斐波那契(1,1,2,3,5...)不同,这里是(1,2,3,5,8...),实际上是斐波那契偏移了一位。

Q3:n=0 时需要处理吗?

A:题目约束n >= 1,不需要处理。但如果扩展,dp[0]=1(有一种方法:不动)也能让递推成立。

Q4:为什么滚动数组可以优化空间?

A:因为计算dp[i]只需要dp[i-1]dp[i-2],不需要保留整个数组。

Q5:n=45 时结果会溢出吗?

A:不会。climbStairs(45) = 1836311903,小于int最大值2^31-1 = 2147483647


扩展思考

变体1:每次可以爬 1, 2, 3 个台阶

java

public int climbStairs(int n) { if (n == 1) return 1; if (n == 2) return 2; if (n == 3) return 4; int prev3 = 1, prev2 = 2, prev1 = 4; for (int i = 4; i <= n; i++) { int current = prev1 + prev2 + prev3; prev3 = prev2; prev2 = prev1; prev1 = current; } return prev1; }

变体2:每次可以爬 1 或 2 个台阶,但不能连续爬 2 个台阶

这需要增加状态维度来记录上一步是爬了 1 阶还是 2 阶。


总结

爬楼梯是 LeetCode Hot 100 中最经典的动态规划入门题,也是斐波那契数列的典型应用。

面试建议:

  1. 先说递归解法(展示思路)

  2. 指出递归的问题(重复计算),引出记忆化搜索

  3. 给出 DP 数组解法

  4. 最后优化到滚动数组(O(1) 空间)

  5. 如果面试官追问,可以提一下矩阵快速幂

核心要点:

  • 理解状态转移方程dp[i] = dp[i-1] + dp[i-2]

  • 掌握空间优化技巧(滚动数组)

  • 能够手推小规模数据验证

一句话总结:

爬到第 n 阶的方法数 = 爬到第 n-1 阶的方法数(再爬 1 阶)+ 爬到第 n-2 阶的方法数(再爬 2 阶)


参考链接

  • LeetCode 70. 爬楼梯 题目链接

  • LeetCode 70. 爬楼梯 官方题解

http://www.jsqmd.com/news/830137/

相关文章:

  • 别再只会用next了!GDB调试实战:用until、finish和jump命令快速定位Linux C/C++程序中的内存泄漏
  • 基于红外对射传感器与Adafruit IO的智能邮箱检测系统实战
  • 告别内网穿透:用动态IPv6与云解析打造永在线的家庭服务器
  • Arduino ESP32终极开发指南:从零开始构建物联网项目
  • LAMMPS分子动力学模拟终极指南:从零开始掌握原子级计算
  • sklearn实战:NearestNeighbors核心参数与算法选择全解析
  • 从狗腿布线到单元上布线:聊聊VLSI物理设计中那些有趣的布线算法(附图解)
  • ESP32深度睡眠后时间怎么同步?SNTP低功耗时间管理保姆级教程
  • 2026年4月专业的盖板模具实力厂家推荐,井盖井篦子模具/装配式围墙模具/标志桩模具/仿古地砖模具,盖板模具厂家有哪些 - 品牌推荐师
  • RouterOS 7.x 虚拟机部署避坑指南:从ISO安装到License激活的完整流程
  • 可穿戴电子圣诞帽制作:NeoPixel灯带与Fosshape面料融合实践
  • 如何构建本地化缠论量化分析平台实现几何交易可视化?
  • 探索Taotoken模型广场如何辅助开发者进行模型选型与切换
  • Steam挂刀行情站:3步实现智能交易决策的开源数据分析工具
  • Nuendo 4.3 声卡设置保姆级教程:从‘No Driver’到完美出声,手把手解决音频工程无声问题
  • FPGA异构计算与模块化SoM:赋能边缘智能与工业应用实战
  • 新手如何通过Taotoken控制台快速创建并管理自己的API Key
  • ROS机器人视觉开发避坑:image_transport发布图片时,为什么你的Topic名字总是不对?
  • 从零构建LAMMPS in文件:分子动力学模拟的完整流程解析
  • 2026年4月本地评价好的HAST试验箱生产厂家推荐分析,高低温交变量热试验箱/砂尘试验箱,HAST试验箱公司推荐分析 - 品牌推荐师
  • MES系统_AI开发
  • Codex安装与VS Code联动
  • 我的文件夹乱到自己都找不到自己,直到我让它学会了自动分类
  • 087、机器人运动学:雅可比矩阵
  • 微信小程序转Vue3完整指南:miniprogram-to-vue3架构深度解析与实战方案
  • DIY冥想训练器:基于心率变异性(HRV)的生物反馈设备制作指南
  • Harness Engineering:用“确定性“驾驭AI的“不确定性“
  • 非现场执法治超系统行业标杆 广州聚杰专注研发铸就高品质设备 - 品牌速递
  • 5步掌握Stable Diffusion 2.1:从零到精通的完整实战指南
  • 地平线X3M平台sensor点亮故障排查实战指南