HARD Lab · Dynamic Programming · 26

70. Climbing Stairs

Each step can be reached from the one or two steps below it, so the number of ways follows the Fibonacci recurrence — and with two rolling variables you solve it in O(n) time and O(1) space.

EasyDPFibonacciTypeScript

PROBLEM What we're solving

You can climb either 1 or 2 stairs at a time. Given n stairs, count the distinct ways to reach the top. n = 5: the answer is 8 (the 5th Fibonacci-like number: 1, 1, 2, 3, 5, 8). For n = 3 the three sequences are 1+1+1, 1+2, 2+13 ways.

KEY IDEA Every step is the sum of its two predecessors

Insight → To reach step i you must have come from step i−1 (one-step jump) or step i−2 (two-step jump). Therefore ways(i) = ways(i−1) + ways(i−2) — the Fibonacci recurrence. The whole problem collapses to a lookup table that fills left-to-right.

RECURRENCE DP recurrence and O(1)-space rolling variables

  • 0 · Base cases. dp[1] = 1 (one way to reach step 1), dp[2] = 2 (1+1 or 2).
  • 1 · Recurrence. dp[i] = dp[i−1] + dp[i−2] for i ≥ 3. Each cell only needs the two before it.
  • 2 · Space crush. Because each step only looks back two positions, keep just two rolling variables (prev2, prev1) and discard the rest. Array size drops from O(n) to O(1).
  • 3 · Answer. Return dp[n] (or prev1 in the rolling version).
Classic confusion → The base cases are dp[1] = 1 and dp[2] = 2, not dp[0] = 0, dp[1] = 1 like standard Fibonacci. Getting these wrong shifts every answer by one. A clean way to avoid the mistake: initialize the rolling variables as prev2 = 1, prev1 = 2 and start the loop at i = 3.

COST Complexity & alternatives

Naive recursion (no memo)
O(2ⁿ)
Re-computes the same sub-problems exponentially.
DP / rolling variables
O(n) / O(1)
Linear time, constant space. Optimal.

The O(n) DP with an explicit array is fine at interview, but the rolling-variable version showcases the insight that only two prior values are needed. There is also a closed-form O(log n) matrix exponentiation approach for very large n, but it is rarely asked.

Pattern transfer → The same recurrence structure powers House Robber (dp[i] = max(dp[i−1], dp[i−2]+nums[i])), Min Cost Climbing Stairs, and Decode Ways(two conditionals feeding the same rolling frame). Any “two choices at each step, no going back” DP reduces to this shape.

RUN IT Build dp[i] = dp[i-1] + dp[i-2] from the ground up

step 0 / 9
STARTInitialize dp[0] = 1 (one way to stand at ground) and dp[1] = 1 (one way to reach step 1). Array is dp[0..5].
dp1011?2?3?4?5
State
5
n
1
dp[0]
1
dp[1]
dp[i] being computedsource cells dp[i-1], dp[i-2]final answerO(1)-space note
slowfast

TYPESCRIPT The solution, annotated

climbStairs.ts
function climbStairs(n: number): number {
  if (n <= 2) return n;
  let prev2 = 1; // ways to reach step 1
  let prev1 = 2; // ways to reach step 2
  for (let i = 3; i <= n; i++) {
    const curr = prev1 + prev2;
    prev2 = prev1;
    prev1 = curr;
  }
  return prev1;
}

Reading it block by block

Line 2 — base cases. For n ≤ 2 the answer is n itself (1 way for n=1, 2 ways for n=2). Returning early keeps the loop simple.
Lines 3–4 — initialize rolling variables. prev2 = 1 holds dp[1] and prev1 = 2 holds dp[2]. These are the two prior values the recurrence always needs.
Lines 5–8 — fill forward. curr = prev1 + prev2 is the recurrence. Then shift: prev2 ← prev1 and prev1 ← curr. After the loop, prev1 holds dp[n].
Line 10 — return. prev1 is the answer. If you used an array you would return dp[n]; the rolling variable is exactly that, compressed.
Complexity → O(n) time (one linear pass), O(1) space (two variables — no array needed). The naive recursive solution without memoization is O(2ⁿ) because it recomputes every subproblem from scratch.

INTERVIEWFollow-ups they'll ask

  • “Can you do it in O(1) space?” Yes — the array is unnecessary because each step only depends on the previous two. prev2 and prev1 are sufficient.
  • “What if you can take 1, 2, or 3 steps?” Extend to dp[i] = dp[i−1] + dp[i−2] + dp[i−3] and keep three rolling variables.
  • “What if each step has a cost?” That's Min Cost Climbing Stairs (LC 746) — add cost[i] inside the recurrence.
  • “Return the actual step sequence?” Keep a parent[] array tracking which predecessor gave the max count, then backtrack from n to reconstruct one valid path.
  • “What's the brute-force?” Plain recursion: climbStairs(n) = climbStairs(n−1) + climbStairs(n−2). It's O(2ⁿ) without memoization — the same sub-problems get recomputed on every branch.

MNEMONIC The one-liner

"You can only arrive from one step below or two below — so ways(i) = ways(i−1) + ways(i−2). It's Fibonacci with a costume."

TRIGGERS When you see ___ → reach for ___

"1 or 2 steps at a time"Fibonacci / rolling-variable DP
count distinct paths with two move sizesdp[i] = dp[i−1] + dp[i−2]
"min cost" or "max loot" at each stepsame frame, swap + for max/min
dp recurrence references only k prior cellsk rolling variables → O(1) space

SKELETON The reusable shape

skeleton.ts
if (n <= 2) return n;
let prev2 = 1; // dp[1]
let prev1 = 2; // dp[2]
for (let i = 3; i <= n; i++) {
  const curr = prev1 + prev2;
  prev2 = prev1;
  prev1 = curr;
}
return prev1;

FLASHCARDS Tap to flip

What is the recurrence for Climbing Stairs?
dp[i] = dp[i−1] + dp[i−2] — ways from one step below plus ways from two steps below.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is climbStairs(5)?
QUESTION 02
What are the correct base cases?
QUESTION 03
Time and space complexity of the O(1)-space rolling solution?
QUESTION 04
In the rolling-variable solution, after the loop prev1 holds:
QUESTION 05
If you can take 1, 2, or 3 steps at a time, the recurrence becomes:
QUESTION 06
Why does climbStairs(n) without memoization run in O(2ⁿ)?
QUESTION 07
climbStairs(1) should return: