Climbing Stairs Solution
You are climbing a staircase with n steps. Each time you can climb 1 or 2 steps. Return the number of distinct ways to reach the top.
Problem Statement
You are climbing a staircase. It takes n steps to reach the top. Each time you can climb either 1 or 2 steps. In how many distinct ways can you climb to the top?
Recursion โ O(2โฟ)
At each step, you have two choices: climb 1 or climb 2. Recursively count all paths: ways(n) = ways(n-1) + ways(n-2). Base cases: ways(0)=1, ways(1)=1.
The recurrence correctly partitions all paths by the first move: paths that start with a 1-step + paths that start with a 2-step. Base cases represent "already at the top" (1 way).
ways(n) recomputes ways(n-2) twice, ways(n-3) three times, etc. Total calls: O(2โฟ). For n=45 that's ~35 billion calls. Memoization or bottom-up DP brings this to O(n) by storing each subproblem once.
| Metric | Value |
|---|---|
| Time | O(2โฟ) โ exponential recursion tree |
| Space | O(n) โ recursion depth |
Bottom-Up DP / Fibonacci โ O(n) time, O(1) space
The recurrence dp[i] = dp[i-1] + dp[i-2] is exactly the Fibonacci sequence. Since each state depends only on the previous two, we only need two variables โ no array required.
- Initialize
a = 1(ways to step 0),b = 1(ways to step 1). - For i from 2 to n:
temp = a + b; a = b; b = temp. - Return
bโ the number of ways to reach step n.
- Any time
dp[i]depends on a fixed number of previous states, you can reduce O(n) space to O(1) by keeping only those states in variables. - This "rolling variables" optimization applies to LC 198 (House Robber), LC 746 (Min Cost Climbing Stairs), and any Fibonacci-like recurrence.
- The signal: "dp[i] = f(dp[i-1], dp[i-2])."
- You might try to count paths using C(n,k) combinations, but the number of 2-steps k can range from 0 to โn/2โ, and each k gives C(n-k, k) arrangements.
- Summing these is more complex and error-prone than the DP approach.
- The Fibonacci recurrence subsumes all these cases automatically.
Visual Walkthrough
Trace for n = 5. Building dp bottom-up with two variables.
Code โ Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Naive recursion | O(2โฟ) | O(n) | Exponential โ TLE at n โฅ ~30 |
| Memoized recursion | O(n) | O(n) | Top-down DP array; correct but uses O(n) space |
| Two-variable Fibonacci โ optimal | O(n) | O(1) | Only two variables; minimal code; constant space |
- Yes โ matrix exponentiation gives O(log n) by raising the Fibonacci transformation matrix to the nth power.
- But for n โค 45, the constant-factor overhead of matrix multiplication makes O(n) faster in practice.
- The O(log n) approach is academic โ interviewers rarely expect it.
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| n = 1 | 1 | Only one step โ one way. Loop doesn't execute. Return b=1. |
| n = 2 | 2 | Two ways: 1+1 or 2. One loop iteration. b=2. |
| n = 0 | Not in constraints | Constraints say n โฅ 1. But if allowed, answer is 1 (already at top). |
| n = 45 (max) | 45 | Fib(46) = 1,836,311,903 โ fits in int (max ~2.1 billion). No overflow. |
| Off-by-one in loop | any | Loop must run from 2 to n (inclusive). Running to n-1 gives dp[n-1] instead of dp[n]. |
| Swapping order wrong | any | If you update a before computing a+b, the sum uses the wrong (already updated) a value. |
a = b; b = a + b; instead of using a temp variable (Java) or simultaneous swap (Python). The first line overwrites a with b, and then the second line computes b + b instead of old_a + b. In Java, use int tmp = a+b; a = b; b = tmp;. In Python, use a, b = b, a+b โ the right side is fully evaluated before assignment.