Climbing Stairs (LeetCode 70): A Tiny Fibonacci in Disguise. A line‑by‑line walkthrough of a 4‑line Python solution, intuition, correctness, and faster alternatives.
Table of Contents
TL;DR
The number of distinct ways to climb n
steps when you can take 1 or 2 at a time follows the Fibonacci numbers. A 4‑line, O(n) time and O(1) space solution uses two variables to iterate the sequence.
def climbStairs(n: int) -> int:
a, b = 1, 1 # f(0), f(1)
for _ in range(n):
a, b = b, a + b
return a
- Intuition: To reach step
n
, you last came from stepn-1
(take 1 step) or from stepn-2
(take 2 steps):f(n) = f(n-1) + f(n-2)
. - Complexity: O(n) time, O(1) space.
Problem
Each time you can climb either 1 or 2 steps. How many distinct ways to reach the top of a staircase with
n
steps?
Constraints: 1 ≤ n ≤ 45
.
Intuition: Why Fibonacci?
Let f(n)
be the number of ways to stand on step n
.
- If your last move was 1 step, you came from
n-1
(there aref(n-1)
ways to be there). - If your last move was 2 steps, you came from
n-2
(there aref(n-2)
ways to be there).
These are disjoint cases, so:
f(n)=f(n−1)+f(n−2)f(n) = f(n-1) + f(n-2)
Base values:
f(0) = 1
(one way to be at the ground: do nothing).f(1) = 1
(one way: a single 1‑step).
This is exactly the Fibonacci recurrence with the shifted base. For the more common Fibonacci definition F(0)=0, F(1)=1
, we have f(n) = F(n+1)
.
Line‑by‑Line Explanation (Python)
def climbStairs(n: int) -> int:
Defines a function that returns the count for a non‑negative integer n
.
a, b = 1, 1 # f(0), f(1)
Initializes two rolling variables:
a
will representf(k)
andb
will representf(k+1)
for the current iterationk
.- Start with
k=0
:a=f(0)=1
,b=f(1)=1
.
for _ in range(n):
Iterate exactly n
times. After i
iterations, the invariant will be: a=f(i)
, b=f(i+1)
.
a, b = b, a + b
One atomic parallel assignment (Python evaluates the right side first):
- New
a
becomes oldb
→ shifts us fromf(k)
tof(k+1)
. - New
b
becomes olda + b
→ computesf(k+2) = f(k+1) + f(k)
.
This advances both variables one step along the Fibonacci sequence without extra memory.
return a
After n
iterations, a = f(n)
— the number of distinct ways to climb n
steps.

Dry‑Run Example (n = 4)
Start: a=1 (f(0))
, b=1 (f(1))
Iteration | a (f(k)) | b (f(k+1)) |
---|---|---|
0 (init) | 1 | 1 |
1 | 1 | 2 |
2 | 2 | 3 |
3 | 3 | 5 |
4 | 5 | 8 |
Return a = 5
→ there are 5 distinct ways for n=4
.
Correctness Argument (Loop Invariant)
Invariant: Before each iteration k
(starting at 0), a = f(k)
and b = f(k+1)
.
- Initialization: At
k=0
,a=1=f(0)
,b=1=f(1)
. - Maintenance: The update
a, b = b, a + b
makes newa = f(k+1)
and newb = f(k) + f(k+1) = f(k+2)
. - Termination: After
n
iterations,a = f(n)
.
Thus, return a
is correct.
Complexity
- Time: O(n) — one pass with constant work per iteration.
- Space: O(1) — only two integers are kept, regardless of
n
.
Note: For the given constraint n ≤ 45
, integer sizes are tiny; overflow is not a concern in Python (arbitrary precision) or 32‑bit signed ints (max result for n=45
is 1,836,311,903
).
Alternatives & Optimizations
1) Fast Doubling (O(log n) time, O(1) space)
A numerically stable method that computes Fibonacci numbers using divide‑and‑conquer identities:
F(2k) = F(k) * [2*F(k+1) − F(k)]
F(2k+1) = F(k+1)^2 + F(k)^2
We return F(n+1)
for f(n)
.
def climbStairs_fast(n: int) -> int:
# returns (F(n), F(n+1)) with F(0)=0, F(1)=1
def fib_pair(m: int):
if m == 0:
return (0, 1)
a, b = fib_pair(m // 2)
c = a * (2 * b - a)
d = a * a + b * b
if m % 2 == 0:
return (c, d)
else:
return (d, c + d)
return fib_pair(n + 1)[0] # F(n+1) = f(n)
2) Combinatorics (Closed Form Sum)
Choosing where the 2‑steps go among (n−k)
moves total when you take k
double steps:
f(n)=∑k=0⌊n/2⌋(n−kk)f(n) = \sum_{k=0}^{\lfloor n/2 \rfloor} \binom{n-k}{k}
Elegant for math insights, but slower to compute directly for large n
.
3) Classic DP Array (O(n) time, O(n) space)
Clear but uses extra memory:
def climbStairs_dp(n: int) -> int:
f = [0] * (n + 2)
f[0], f[1] = 1, 1
for i in range(2, n + 1):
f[i] = f[i - 1] + f[i - 2]
return f[n]
Edge Cases & Pitfalls
- n = 1: Returns 1 (only one 1‑step).
- n = 2: Returns 2 (1+1, 2).
- Off‑by‑one mistakes: If you start with
(a, b) = (0, 1)
you must returnb
at the end; with(1, 1)
you returna
. - Do not build all paths: The count grows exponentially; enumerating paths is unnecessary and expensive.
Testing (pytest)
import pytest
from typing import Callable
SOLS: list[Callable[[int], int]] = []
# drop your implementations here
def climbStairs(n: int) -> int:
a, b = 1, 1
for _ in range(n):
a, b = b, a + b
return a
SOLS.append(climbStairs)
def climbStairs_fast(n: int) -> int:
def fib_pair(m: int):
if m == 0:
return (0, 1)
a, b = fib_pair(m // 2)
c = a * (2 * b - a)
d = a * a + b * b
return (c, d) if m % 2 == 0 else (d, c + d)
return fib_pair(n + 1)[0]
SOLS.append(climbStairs_fast)
@pytest.mark.parametrize("n, expected", [
(1, 1), (2, 2), (3, 3), (4, 5), (5, 8), (10, 89), (45, 1836311903)
])
def test_climb_stairs(n, expected):
for f in SOLS:
assert f(n) == expected
Variations You’ll See in Interviews
- Different step sets: Allowed steps are
{1, 3, 5}
or any setS
. Usedp[i] = sum(dp[i-s] for s in S if i-s >= 0)
. - Exactly k steps: Count ways to use exactly
k
moves — use combinatorics or 2D DP. - Min cost to reach the top: Each step has a cost; compute min cost with DP (
min(dp[i-1], dp[i-2]) + cost[i]
).
Takeaways
- The staircase problem is Fibonacci in disguise.
- A simple two‑variable loop gives optimal space and clear correctness.
- Fast doubling offers a clean O(log n) upgrade when
n
is large.
About Connect Kreations
At Connect Kreations, we bridge the gap between technology and the community — simplifying complex ideas with practical code and hands‑on learning. If you found this useful, share it with a fellow learner and explore more guides on algorithms and interview prep.
Tags: algorithms, dynamic programming, fibonacci, interview‑prep, python, leetcode-70