从斐波那契到爬楼梯:用Python三种方法搞定LeetCode 70题(附复杂度分析)
·
从斐波那契到爬楼梯:用Python三种方法搞定LeetCode 70题(附复杂度分析)
第一次在LeetCode上遇到爬楼梯问题时,我盯着屏幕上的台阶数n,脑海里闪过无数种组合方式。直到发现每次只能迈1或2个台阶的规则后,突然意识到——这不就是斐波那契数列换了个马甲吗?今天我们就用Python这把瑞士军刀,解剖这道看似简单却暗藏玄机的经典算法题。
1. 问题本质与数学建模
站在楼梯底部仰望n级台阶时,最后一步只有两种可能:从n-1级跨1阶,或从n-2级跨2阶。这个关键观察将问题转化为:
f(n) = f(n-1) + f(n-2)
这正是斐波那契数列的递推公式。让我们用数学归纳法验证几个基础案例:
| 台阶数n | 可行方案 | 方案数f(n) |
|---|---|---|
| 1 | [1] | 1 |
| 2 | [1+1], [2] | 2 |
| 3 | [1+1+1], [1+2], [2+1] | 3 |
| 4 | [1+1+1+1], [1+1+2], [1+2+1], [2+1+1], [2+2] | 5 |
这个建模过程揭示了算法设计的核心—— 将实际问题抽象为已知数学模型 。理解这一点,后续的代码实现就水到渠成了。
2. 递归解法:直观但低效
最直接的实现方式是递归,完美对应数学定义:
def climb_stairs_recursive(n):
if n <= 2:
return n
return climb_stairs_recursive(n-1) + climb_stairs_recursive(n-2)
复杂度分析 :
- 时间复杂度:O(2^n) —— 递归树呈指数级膨胀
- 空间复杂度:O(n) —— 递归栈深度
实际测试时,当n=40时计算时间已超过10秒。这是因为存在大量重复计算,比如计算f(5)时需要f(4)和f(3),而f(4)又需要f(3)和f(2),其中f(3)被计算了两次。
提示:面试时如果只给出这种解法,可能会被追问优化方案
3. 记忆化递归:空间换时间的艺术
引入缓存机制避免重复计算:
def climb_stairs_memo(n, memo={}):
if n <= 2:
return n
if n not in memo:
memo[n] = climb_stairs_memo(n-1, memo) + climb_stairs_memo(n-2, memo)
return memo[n]
优化效果 :
- 时间复杂度降为O(n) —— 每个子问题只计算一次
- 空间复杂度保持O(n) —— 需要存储所有中间结果
实测n=45时仅需0.03毫秒。这种 自上而下 的解法特别适合问题具有重叠子特性的场景。
4. 动态规划:从记忆化到迭代
将递归转为迭代,采用 自底向上 的填表法:
def climb_stairs_dp(n):
if n <= 2:
return n
dp = [0]*(n+1)
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
虽然时间复杂度仍为O(n),但消除了递归开销。不过空间使用可以进一步优化——我们其实只需要前两个状态。
5. 滚动数组:极致的空间优化
只维护必要的中间变量:
def climb_stairs_optimal(n):
if n <= 2:
return n
a, b = 1, 2
for _ in range(3, n+1):
a, b = b, a + b
return b
最终优化指标 :
- 时间复杂度:O(n)
- 空间复杂度:O(1)
这是面试官最期待的解法,体现了对问题本质的深刻理解。在Python中还可以用生成器实现更优雅的写法:
def fib_gen():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
def climb_stairs(n):
return next(x for i,x in enumerate(fib_gen()) if i==n)
6. 面试实战策略
当被问到这道题时,建议分阶段回答:
- 问题分析 :指出斐波那契数列的关联性
- 逐步优化 :
- 先给出递归解法
- 分析缺陷后引入记忆化
- 转为动态规划
- 最终空间优化
- 扩展讨论 :
- 如果每次可以爬1/2/3个台阶?递推式变为f(n)=f(n-1)+f(n-2)+f(n-3)
- 使用矩阵快速幂可将时间复杂度优化到O(log n)
# 快速幂解法示例
def matrix_pow(mat, power):
result = [[1 if i==j else 0 for j in range(len(mat))] for i in range(len(mat))]
while power > 0:
if power % 2 == 1:
result = matrix_mult(result, mat)
mat = matrix_mult(mat, mat)
power //= 2
return result
def climb_stairs_matrix(n):
if n <= 2: return n
mat = [[1,1], [1,0]]
return matrix_pow(mat, n)[0][0]
记住,面试官考察的不仅是解法,更是你 分析问题、优化方案的思维过程 。这道题就像算法世界的Hello World,简单却蕴含着动态规划的精髓。
更多推荐
所有评论(0)