011 · Climbing Stairs

algorithm
Published

April 29, 2026

My First Thoughts

第一眼看这个题,很容易把它想成一个排列组合问题:

一共有 n 阶楼梯,每次可以走 1 阶或 2 阶。那是不是只要看用了几个 2,再把这些 2 和剩下的 1 排列起来?

比如 n = 4

1 + 1 + 1 + 1
1 + 1 + 2
1 + 2 + 1
2 + 1 + 1
2 + 2

确实可以这样数。

如果用了 k2,那么这些 2 一共走了 2k 阶,剩下还需要 n - 2k1

这一组走法里,一共有:

(n - 2k) + k = n - k

个步子。

其中要选出 k 个位置放 2,其他位置放 1。所以这一组的数量是:

C(n - k, k)

把所有可能的 k 加起来,就能得到答案:

C(n, 0) + C(n - 1, 1) + C(n - 2, 2) + ...

这里的 k 最多到 floor(n / 2),因为 2 的数量不可能超过楼梯阶数的一半。

这个数学思路是对的。


Why That Is Not Enough

不过如果真的按组合公式来写,就需要额外实现组合数计算。

这对这道题来说有点绕。

题目并不要求我们列出所有走法,也不要求我们专门推一个组合公式。它只问:

到第 n 阶有多少种方法?

这句话其实更适合从“最后一步”倒着看。

要到达第 n 阶,最后一步只有两种可能:

  1. 从第 n - 1 阶走 1 阶上来。
  2. 从第 n - 2 阶走 2 阶上来。

这两类情况不会重叠。

所以,到第 n 阶的方法数,就是:

到第 n - 1 阶的方法数 + 到第 n - 2 阶的方法数

也就是:

ways(n) = ways(n - 1) + ways(n - 2)

这就是 Fibonacci sequence,也就是斐波那契数列的结构。

最直接的代码可能会写成递归:

def climb_stairs(n: int) -> int:
    if n == 1:
        return 1
    if n == 2:
        return 2

    return climb_stairs(n - 1) + climb_stairs(n - 2)

这个递归表达了正确的关系,但它会重复计算很多次。

比如计算 climb_stairs(5) 时,会计算:

climb_stairs(4) + climb_stairs(3)

climb_stairs(4) 里面又会继续计算:

climb_stairs(3) + climb_stairs(2)

这里 climb_stairs(3) 已经被重复算了。

n 变大时,重复计算会越来越多。


Final Idea

既然每一阶只依赖前两阶,就不需要真的递归展开。

我们可以从小到大算:

ways(1) = 1
ways(2) = 2
ways(3) = ways(2) + ways(1) = 3
ways(4) = ways(3) + ways(2) = 5
ways(5) = ways(4) + ways(3) = 8

而且算 ways(i) 的时候,只需要知道:

  • 前一阶的方法数
  • 前两阶的方法数

所以不需要保存整个数组,用两个变量就够了。


Why It Works

prev2 表示到达第 i - 2 阶的方法数,prev1 表示到达第 i - 1 阶的方法数。

那么到达第 i 阶的方法数就是:

current = prev1 + prev2

因为最后一步如果走 1 阶,就来自 i - 1;如果走 2 阶,就来自 i - 2

这两种最后一步不同,所以不会重复计数。

算出 current 后,继续往后推进:

prev2 = prev1
prev1 = current

这样每一轮都保持:

  • prev2 是当前目标前两阶的方法数
  • prev1 是当前目标前一阶的方法数

直到算到第 n 阶。


Code

def climb_stairs(n: int) -> int:
    if n == 1:
        return 1
    if n == 2:
        return 2

    prev2 = 1
    prev1 = 2

    for _ in range(3, n + 1):
        current = prev1 + prev2
        prev2 = prev1
        prev1 = current

    return prev1

Complexity

Time \(O(n)\) — 从第 3 阶一路算到第 n
Space \(O(1)\) — 只保留前两阶的方法数

Takeaway

当一个问题看起来像排列组合时,可以先尝试数“用了几个特殊元素”。但如果题目只问总数量,而且当前状态只依赖前面的少数状态,通常可以转成动态规划。这里的关键不是列出所有走法,而是看清楚:到达第 n 阶的最后一步,只可能来自第 n - 1 阶或第 n - 2 阶。


Quiz