Home DSA Climbing Stairs Dynamic Programming: 5 Easy Wins for Newbies
Beginner 3 min · September 07, 2026

Climbing Stairs Dynamic Programming: 5 Easy Wins for Newbies

Climbing Stairs DP in O(n) time, O(1) space.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.

Follow
Production
production tested
September 22, 2026
last updated
1,799
articles · all by Naren
Before you start⏱ 10 min
  • Recursion and overlapping subproblems
  • Fibonacci sequence intuition
  • Big-O time and space analysis
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Climbing Stairs asks for distinct ways to reach step n taking 1 or 2 steps (e.g. n = 5 gives 8)
  • Optimal approach: bottom-up with two rolling variables — O(n) time, O(1) space
  • Key trick: classify by the last move (from n-1 or n-2) so ways(n) = ways(n-1) + ways(n-2) with seeds 1, 2
  • Asked at Amazon, Google, and Meta in most DP rounds — the standard first-DP gateway
✦ Definition~90s read
What is Climbing Stairs Dynamic Programming?

Climbing Stairs is LeetCode 70, an Easy DP problem and the standard first dynamic-programming interview question. Given n up to 45, you count ordered ways to climb taking 1 or 2 steps. It appears at Amazon, Google, Meta, and Microsoft because it tests recurrence derivation, overlap recognition, and space compression in five lines.

Imagine a staircase where you may climb one or two steps at a time.

The solution classifies paths by their last move — from n-1 or n-2 — giving ways(n) = ways(n-1) + ways(n-2) with seeds 1, 2. Bottom-up computation with two rolling variables yields O(n) time and O(1) space. The last-move partition pattern transfers directly to House Robber, Min Cost Climbing Stairs, and coin-change counting, making this tiny problem the foundation of DP interview fluency.

Plain-English First

Imagine a staircase where you may climb one or two steps at a time. To reach step 10, your last move came either from step 9 (a single) or step 8 (a double). So the number of ways to reach step 10 equals the ways to reach step 9 plus the ways to reach step 8. Work forward from the bottom: 1 way to reach step 1, 2 ways to reach step 2, then each new step adds the previous two counts. You only ever need the last two numbers, so a scrap of paper with two slots replaces a whole notebook.

Climbing Stairs is everyone's first DP problem. It's also where recursion habits go to die.

The naive code mirrors the story: ways(n) = ways(n-1) + ways(n-2). Clean, correct, catastrophic. It recomputes the same subproblems millions of times. At n = 40 it makes 100M+ calls. You'll watch it hang on a trivial input.

The fix is five lines bottom-up. Two rolling variables, one loop from 3 to n. That's O(n) time, O(1) space. Don't just memorize the loop — derive the recurrence from the last-step choice and you'll own every DP follow-up.

Counting Distinct Ways, Not Finding a Shortest Path

Given n stairs, count distinct ways to reach the top taking 1 or 2 steps at a time. Order matters: 1+2 and 2+1 are different ways. n = 2 gives 2 ([1,1], [2]); n = 3 gives 3 ([1,1,1], [1,2], [2,1]); n = 5 gives 8.

Constraints: 1 ≤ n ≤ 45. The cap exists because ways(45) = 1,836,311,903 fits 32-bit signed int — the problem is designed around Fibonacci growth. Return a count, not the paths (listing them would be exponential).

Walk n = 4 by last move: paths ending with a single arrive from step 3 (3 ways); paths ending with a double arrive from step 2 (2 ways). Total 5: [1,1,1,1], [1,1,2], [1,2,1], [2,1,1], [2,2]. The last-move split is the whole derivation.

📊 Production Insight
List all 5 paths for n = 4 on paper first. Candidates who enumerate concretely never write the recurrence backwards.
🎯 Key Takeaway
Count ordered 1/2-step paths; n = 4 gives 5 — split by last move before coding.

Naive Recursion Recomputes the Same Step 2^n Times

Naive recursion returns ways(n-1) + ways(n-2) directly. Correct, and O(2^n) time: ways(40) triggers ~200M calls because ways(k) is recomputed ~2^(n-k) times. Space is O(n) stack depth. At n = 45 (~2 billion calls) it never finishes; past n = 1000 it also hits Python's recursion limit.

The call tree shows the disease: ways(5) recomputes ways(3) three times and ways(2) five times. Exponential redundancy from overlapping subproblems — the textbook DP symptom.

Interview play: write the recurrence, name the overlap, and refuse to submit it. 'Naive recursion is O(2^n) on overlap — I'll build bottom-up.' That sentence is the DP password.

📊 Production Insight
Draw the call tree for ways(5) showing ways(3) computed 3 times. Interviewers accept the drawing as proof you understand overlap.
🎯 Key Takeaway
Unmemoized recursion costs O(2^n) — n = 40 needs ~200M calls; name the overlap, don't submit it.

Bottom-Up DP: Each Step Is the Sum of the Previous Two

Classify all paths by their last move: from n-1 via single, or from n-2 via double. The classes are disjoint and exhaustive, so ways(n) = ways(n-1) + ways(n-2). Seeds: ways(1) = 1 ([1]), ways(2) = 2 ([1,1],[2]). Each state depends only on solved smaller states — compute from 3 upward, once each: O(n) time.

Proof sketch by induction: base seeds hold by enumeration. Assume all values below n correct; the last-move partition counts every n-path exactly once (each path has exactly one last move), so the sum is exact. Rolling variables keep only the two predecessors the recurrence reads: first, second = 1, 2, then per step nxt = first + second with a shift. Space drops from O(n) table to O(1) with identical results.

This is Fibonacci shifted by one index (ways(n) = Fib(n+1)) — but derive it live rather than quoting. Derivation beats recall in scoring.

📊 Production Insight
Say 'partition by last move' explicitly. Those four words signal you derive recurrences instead of memorizing them.
🎯 Key Takeaway
Last-move partition gives the recurrence; induction proves it; rolling pair compresses space to O(1).

The Bottom-Up DP Solution in Full Python

The code above is the complete LeetCode submission. Trace n = 5: guard skipped; (1,2) → step3: (2,3) → step4: (3,5) → step5: (5,8). Returns 8. Trace n = 1: guard returns 1. n = 2: guard returns 2.

Complexity: O(n) time (one loop), O(1) space (two ints). n = 45 returns 1836311903 instantly. No imports, no recursion, no tables.

solution.pyPYTHON
1
2
3
4
5
6
7
8
class Solution:
    def climbStairs(self, n: int) -> int:
        if n <= 2:
            return n
        first, second = 1, 2
        for _ in range(3, n + 1):
            first, second = second, first + second
        return second
⚠ The Exponential Trap
Naive recursion is O(2^n) — n = 40 makes ~200M calls. Bottom-up with two variables is O(n)/O(1). Never submit the tree.
📊 Production Insight
Trace n = 5 live showing the pair sliding (1,2)→(2,3)→(3,5). The sliding visual sells the O(1)-space claim better than words.
🎯 Key Takeaway
Two variables, one loop, O(n)/O(1) — verify n = 1..5 by hand before submitting.

n = 1, n = 2 and Why the Base Cases Decide Everything

n = 1 returns 1, n = 2 returns 2 — the guard handles both. n = 45 (max) returns 1134903170 with no overflow in Python (mention 32-bit fit for Java/C++ follow-ups). Loop bounds range(3, n+1) execute zero times at n ≤ 2, but the guard already returned.

Tuple-assignment order matters: first, second = second, first + second evaluates the right side fully before rebinding. Sequential assignment (first = second; second = first + second) reuses the new first — the classic drift bug. Recursion variants need sys.setrecursionlimit past n = 1000; the loop has no such ceiling.

📊 Production Insight
Python's tuple assignment evaluates right-first — state that when writing the update line. It preempts the most common live-coding drift bug.
🎯 Key Takeaway
Test n = 1, 2, 3, 5, 45 — five values cover guards, seeds, loop, and max input.

Why Two Variables Replace the Entire DP Table

Time O(n): one pass from 3 to n with O(1) per step. Space O(1): two integers regardless of n. Could O(log n) matrix exponentiation win? Asymptotically yes, practically no — for n ≤ 45 the loop is ~40 additions, clearer and already instant.

Whiteboard closer: 'Partition by last move, build bottom-up, keep two variables.' Then bridge: 'House Robber uses the same skeleton with a max instead of a sum; Min Cost Climbing Stairs adds per-step costs.' Two follow-ups answered before they're asked.

📊 Production Insight
Close by volunteering House Robber as the next problem. Interviewers track who sees DP families vs isolated tricks.
🎯 Key Takeaway
O(n) time is one pass over states; O(1) space keeps only what the recurrence reads — practical optimum.
● Production incidentPOST-MORTEMseverity: high

The 18-Minute Hang on n = 40 That Taught Memoization

Symptom
n = 5 returned 8 correctly (slowly), n = 40 froze the editor for 30+ seconds, and the table rewrite returned 2 for n = 3 instead of 3.
Assumption
The candidate assumed the recurrence-shaped recursion was automatically efficient — 'it IS dynamic programming,' they said — never noticing that without memoization each subproblem is recomputed exponentially many times.
Root cause
Unmemoized recursion recomputes ways(k) ~2^(n-k) times: n = 40 needs ~200M calls (hang), and the first table attempt seeded ways(0)=0/ways(1)=0, shifting every answer by one.
Fix
With 14 minutes left the interviewer asked for the call count at n = 40. The candidate estimated 100M+, added the bottom-up loop with rolling variables in 4 minutes, and passed — graded as hire-leaning for honest recovery.
Key lesson
  • Recurrence-shaped code is not DP until each subproblem is solved once — say 'overlapping subproblems' and 'compute bottom-up' explicitly.
  • Always volunteer the O(1)-space rolling pair; interviewers score unprompted optimization as seniority.
Production debug guideFour defect shapes and the exact check that exposes each one.4 entries
Symptom · 01
Every answer is one Fibonacci index off
Fix
Print the rolling pair each iteration for n=5: expect (1,2) → (2,3) → (3,5). If values lag by one index, the seeds are swapped — set first=1, second=2 and loop from 3.
Symptom · 02
Hangs on n = 35+ or RecursionError on large n
Fix
Time n=40: over 2 seconds means raw recursion. Convert to the bottom-up loop, or add memoization and confirm n=44 returns 1134903170 instantly.
Symptom · 03
n = 1 or n = 2 returns the wrong count
Fix
Add 'if n <= 2: return n' at the top. Verify n=1 → 1, n=2 → 2, n=3 → 3 before touching larger cases.
Symptom · 04
Small n right, larger n drifts (update-order bug)
Fix
Confirm the update order: nxt = first + second, then first, second = second, nxt. Reversed assignment reuses the new value twice — trace n=3 by hand to catch it.
Climbing Stairs Approaches Compared
ApproachTimeSpaceVerdict
Naive recursion (no memo)O(2^n)O(n) stackDead past n = 40
Memoized recursion (top-down)O(n)O(n)Good, teaches the pattern
Tabulation array (bottom-up)O(n)O(n)Good, no recursion limit
Rolling two variablesO(n)O(1)Best: optimal, five lines

Key takeaways

1
Climbing Stairs counts paths where the last move comes from n-1 or n-2
ways(n) = ways(n-1) + ways(n-2).
2
Naive recursion is O(2^n) on overlapping subproblems; bottom-up computes each state once
O(n).
3
Rolling two variables cut O(n) table space to O(1)
always offer this unprompted.
4
Guard n <= 2 with 'return n'
seeds ways(1)=1, ways(2)=2 align the whole loop.
5
The last-move classification pattern transfers to House Robber, coin change, and k-step variants.

Common mistakes to avoid

4 patterns
×

Writing the recurrence backwards or with wrong seeds

Symptom
n=2 returns 1, n=3 returns 2 — every answer is one Fibonacci index off. The seeds first=1, second=2 fix the alignment.
Fix
Define ways[n] = ways[n-1] + ways[n-2] from the last-step choice: arrive from n-1 (1 step) or n-2 (2 steps). Seed first = 1 (ways to reach step 1), second = 2 (ways to reach step 2), loop from 3.
×

Submitting naive recursion without memoization

Symptom
n=40 hangs (100M+ redundant calls); n > 1000 crashes with RecursionError. LeetCode caps n at 45, where naive recursion needs ~2 billion calls.
Fix
Iterate bottom-up from 3 to n with two rolling variables. Reserve memo recursion for explaining the pattern, then convert — or add @lru_cache plus sys.setrecursionlimit, but the loop is strictly better.
×

Allocating a full O(n) table and stopping there

Symptom
Passes, but the interviewer asks 'can you cut the memory?' and the candidate stalls. Rolling variables answer it in two lines.
Fix
Keep the O(1)-space rolling pair as the final code. Mention the O(n) table only as the teaching step that derives it. Space optimization is a standard explicit follow-up.
×

Off-by-one on n = 1 and n = 2 base cases

Symptom
n=1 returns 0 or 2, n=2 returns 1 or 3. Hidden tests always include both — one guard line kills the whole class.
Fix
Return n for n <= 2 via 'if n <= 2: return n' — check: n=1 → 1, n=2 → 2. Loop range(3, n+1) runs zero times for small n, so the guard covers everything.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Derive the recurrence from scratch.
Q02SENIOR
How do you cut the DP table from O(n) to O(1) space?
Q03SENIOR
Generalize to k step sizes, or add per-step costs?
Q01 of 03JUNIOR

Derive the recurrence from scratch.

ANSWER
Classify by the final move: single step from n-1 or double step from n-2. These classes are disjoint and cover everything, so Ways(n) = Ways(n-1) + Ways(n-2). Seeds Ways(1)=1, Ways(2)=2. Overlapping subproblems (Ways(3) recomputed many times) make naive recursion exponential; computing bottom-up once each gives O(n).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Why is this Fibonacci in disguise?
02
Can I list all the ways in O(n)?
03
What if I can take 1, 2, or 3 steps?
04
Does recursion depth matter here?
05
Is there a faster-than-O(n) solution?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.

Follow
Verified
production tested
September 22, 2026
last updated
1,799
articles · all by Naren
🔥

That's Dynamic Programming. Mark it forged?

3 min read · try the examples if you haven't

Previous
Container With Most Water
16 / 17 · Dynamic Programming
Next
Trapping Rain Water Problem