LeetCode #70 Easy

Climbing Stairs

Climbing Stairs is LeetCode 70 (Easy). A staircase has n steps and you start at the bottom. Each move climbs either 1 step or 2 steps. Return how many distinct ways there are to reach the top.

Two ways to climb stairs are different when their sequences of moves differ, so order matters.

Constraints
  • 1 <= n <= 45
dynamic-programmingrecursionmaths
Open on LeetCode ↗
02

Intuition

Do not try to build routes from the bottom. Look at the last move instead. To stand on step n, your final move was either a 1-step from n-1 or a 2-step from n-2. Every route to n-1 extends to n in exactly one way, and so does every route to n-2. No route is in both groups, because the last move differs, so the two counts simply add.

That makes the number of ways to climb stairs the Fibonacci recurrence: each value is the sum of the two before it. Since nothing older is ever needed, this climbing stairs dynamic programming solution only has to remember two numbers.

How to spot this pattern

A count of ways to reach a target, where each move uses a small fixed set of sizes and order matters. Split by the last move and add the groups. With moves {1, 2} it is Fibonacci; with moves {1, 2, 3} it is the same idea with three terms.

03

Approach

Try it first

Before reading on, count the ways for n = 1, 2, 3 and 4 by hand. Do you see how each answer is built from the previous two? Aim for O(n) time and O(1) space.

1

Define the count by the last move

Let ways(i) be the number of distinct routes that end on step i, so the answer is ways(n). Splitting the routes by their last move gives ways(i) = ways(i-1) + ways(i-2), which depends only on answers for lower steps.

2

Fix the base cases

  • ways(0) = 1: the empty route (standing still).
  • ways(1) = 1: a single 1-step.

Check: ways(2) = 1 + 1 = 2, matching 1+1 and 2. Using ways(0) = 0 shifts every later answer.

3

Avoid plain recursion

Calling f(n-1) + f(n-2) recursively recomputes the same steps again and again: about 2^n calls, far too slow for n = 45. Either memoise it (cache each f(i)) or build the answers upward in a loop.

4

Keep two variables instead of an array

Walk i from 2 to n holding prev = ways(i-2) and cur = ways(i-1), and set prev, cur = cur, prev + cur each time. Only the last two values feed the next one, so an array of size n would be wasted memory: time stays O(n) and space drops to O(1).

04

Climbing Stairs solution in Python | C++ | Java

▶1class Solution:
▶2 def climbStairs(self, n: int) -> int:
▶3 prev, cur = 1, 1
▶4 for _ in range(2, n + 1):
▶5 prev, cur = cur, prev + cur
▶6 return cur
step 01step 11step 2step 3step 4step 5
prev1ways to stand on step 0
cur1ways to reach step 1
Base cases. There is exactly one way to be on step 0 (do nothing) and one way to reach step 1 (a single 1-step). Step 0 must be 1, not 0: it is the empty route that a 2-step onto step 2 extends.
step 01step 11step 22step 3step 4step 5+1+2
step2last move was +1 or +2
prev1ways(1)
cur21 + 1 = ways(2)
Every route to step 2 ends with a 1-step from step 1 (1 routes) or a 2-step from step 0 (1 routes). The two groups never overlap because their last move differs, so they add: 1 + 1 = 2. Only these two numbers are needed, which is why two variables replace the whole array.
step 01step 11step 22step 33step 4step 5+1+2
step3last move was +1 or +2
prev2ways(2)
cur32 + 1 = ways(3)
Every route to step 3 ends with a 1-step from step 2 (2 routes) or a 2-step from step 1 (1 routes). The two groups never overlap because their last move differs, so they add: 2 + 1 = 3. Only these two numbers are needed, which is why two variables replace the whole array.
step 01step 11step 22step 33step 45step 5+1+2
step4last move was +1 or +2
prev3ways(3)
cur53 + 2 = ways(4)
Every route to step 4 ends with a 1-step from step 3 (3 routes) or a 2-step from step 2 (2 routes). The two groups never overlap because their last move differs, so they add: 3 + 2 = 5. Only these two numbers are needed, which is why two variables replace the whole array.
step 01step 11step 22step 33step 45step 58+1+2
step5last move was +1 or +2
prev5ways(4)
cur85 + 3 = ways(5)
Every route to step 5 ends with a 1-step from step 4 (5 routes) or a 2-step from step 3 (3 routes). The two groups never overlap because their last move differs, so they add: 5 + 3 = 8. Only these two numbers are needed, which is why two variables replace the whole array.
step 01step 11step 22step 33step 45step 58ends +111111211112111121221ends +211122121228 ways → return 8
answer8ways to reach step 5
Answer 8. All 8 routes are drawn below, split by their last move: 5 end with a 1-step and 3 with a 2-step, exactly the two terms that were added. The counts 1, 1, 2, 3, 5, 8 are the Fibonacci numbers, because each one is the sum of the two before it.
05

Memoized recursion

ways(i) returns 1 for steps 0 and 1, and otherwise adds ways(i-1) and ways(i-2). The cache stores each answer the first time, so later calls return it at once.

▶1from functools import cache
▶2 
▶3 
▶4class Solution:
▶5 def climbStairs(self, n: int) -> int:
▶6 @cache
▶7 def ways(i: int) -> int:
▶8 if i <= 1:
▶9 return 1
▶10 return ways(i - 1) + ways(i - 2)
▶11 
▶12 return ways(n)
06

Common pitfalls

Recursing without memoisation

✗ Wrong
def climbStairs(self, n):
    if n <= 1:
        return 1
    return self.climbStairs(n - 1) + self.climbStairs(n - 2)
✓ Right
prev, cur = 1, 1
for _ in range(2, n + 1):
    prev, cur = cur, prev + cur

The recursion tree has about 2^n nodes because f(n-2) is computed inside f(n-1) and again on its own. At n = 45 that is billions of calls and a time-out. The loop computes each value once.

Starting from ways(0) = 0

✗ Wrong
prev, cur = 0, 1
✓ Right
prev, cur = 1, 1

The empty route to step 0 is what a 2-step onto step 2 extends. With 0 there, ways(2) comes out as 1 and every answer is shifted by one position.

Updating the two variables one after the other

✗ Wrong
prev = cur
cur = prev + cur
✓ Right
prev, cur = cur, prev + cur

The first line overwrites prev before the second reads it, so cur doubles each time and you get powers of two. Use simultaneous assignment in Python or a next temporary in C++ and Java.

07

Edge cases

n = 1

Return 1. The two-variable loop simply does not run. An array version that writes dp[2] would index past a size-2 array, which is why array solutions special-case it.

n = 45 (the maximum)

The answer is 1,836,311,903, which fits in a 32-bit signed int. From about n = 46 it would overflow int.

08

Complexity

Time
O(n)
Space
O(1)
One pass with two rolling variables. Plain recursion is O(2^n); memoised recursion is O(n) time but O(n) stack and cache.
09

Climbing Stairs FAQ

What is the climbing stairs problem?

Given n stairs, where each move climbs 1 or 2 steps, count the distinct ordered sequences of moves that reach the top. For n = 3 the answer is 3: 1+1+1, 1+2, 2+1.

How does the climbing stairs solution work, step by step?
  • State: ways(i) = number of distinct routes to step i.
  • Recurrence: the last move is +1 from i-1 or +2 from i-2, and these groups are disjoint, so ways(i) = ways(i-1) + ways(i-2).
  • Base cases: ways(0) = 1, ways(1) = 1.
  • Computation: loop i from 2 to n keeping two variables.
  • Complexity: O(n) time, O(1) space.
  • Example: n = 5 gives 8.
Why is climbing stairs a Fibonacci problem?

Because each answer is the sum of the two previous answers, and the starting values are 1 and 1. That is the definition of the Fibonacci sequence, so ways(n) = F(n+1).

Is climbing stairs dynamic programming?

Yes. It has overlapping subproblems (the same ways(i) is needed many times) and optimal substructure (the answer for n is built from answers for smaller steps). The loop is bottom-up DP with the table reduced to two variables.