top of page

Climbing Stairs

  • mosesg1123
  • 6 days ago
  • 5 min read

Climbing stairs quietly tests whether you recognize overlapping subproblems and know when brute force turns exponential. It’s a perfect warm-up for dynamic programming and a great checkpoint for whether recursion is helping or hurting you.


Problem Statement

You are climbing a staircase with n steps. Each time, you can climb either 1 step or 2 steps.

Return the total number of distinct ways you can reach the top.


Example:

Input: n = 5
Output: 8

Visualization for n = 4:

Step 0 -> 1 -> 2 -> 3 -> 4

Ways:
1-1-1-1
1-1-2
1-2-1
2-1-1
2-2

1. Clarify Requirements Before Jumping Into Code

Let's start by locking down the expectations:

  • n is a non-negative integer

  • I can only take steps of size 1 or 2

  • Order matters, since different sequences count as different ways

  • This is not asking for the paths themselves, just the count

I also want to confirm whether n = 0 should return 1. Most versions define standing at the top as one valid way.


2. Identify the Category of the Question

This problem exhibits one of the classic signals of a Dynamic Programming problem: Overlapping subproblems. Why? Because counting paths is a classic example of dynamic programming.


Common patterns for this category include:

  • Top-down recursion with memoization

  • Bottom-up DP array

  • Space-optimized DP using rolling variables


3. Consider a Brute-Force Approach to Understand the Problem

Let's imagine that I'm on some step n. What are my options for continuing up the staircase?

Well, I can either:

  • Take 1 step, or

  • Take 2 steps


We can flip that to figure out that to get to step n, I have to be at either step n-1 or I can be at step n-2.

If I know all of the possible paths to get to step n-1 and all of the possible paths to get to step n-2, then I can simply add those paths up and I have my solution.

How do I know all the possible paths to get to n-1 and n-2? Well, I keep going backwards until n=1, because I know how many paths there are to get to step n=1: there's just one.


What does this all sound like? It sounds like recursion!


So the total number of paths to reach step n is:

paths(n) = paths(n - 1) + paths(n - 2)

But, there's a problem.

I'm doing a lot of recalculations. The time complexity explodes to O(2ⁿ) because of all that extra work, and that quickly becomes unusable even for modest values of n.

It works conceptually, but not practically.


4. Brainstorm More Solutions

So, now that we have a starting point, let's tighten up our solution.


Step 1: Break the problem into smaller subproblems

When solving a Dynamic Programming problem like this one, we want to start by identifying the subproblems that we are trying to solve. We did that in our brute-force recursive solution.


To reach step n, I must come from either:

  • Step n - 1

  • Step n - 2

So if I already knew the number of paths to reach those steps, the answer for n is immediate.

That’s the key insight.


Step 2: Look for repeated work

Looking at our recursive solution, we can quickly identify that we are repeating the same calculations: paths(n-1) and paths(n-2) are each being called multiple times for the same inputs.

If we stored the results to those method calls, then we would save substantial amounts of computational power.

In other words, we want to try memoization!


Option 1: Try memoized recursion (Top-Down DP)

I can store the results of each paths(k) calculation so that it is only ever computed once. My recursive solution only has to change one minor way: before I do any calculations, I reference the array for the solution. If I've already calculated paths(k), I can return it immediately from my cache.

This drops the time complexity to O(n) and keeps the recursive structure intact.

While this works, we should also remember recursions key drawback: it adds overhead and stack depth. I may have saved computational power, but stack depth could still be a limiting factor.


Option 2: Flip it into bottom-up DP

Since a top-down DP solution requires recursion, what if we started from the bottom-up instead? It's easy to start: I know there's only 1 path to reach step 0, and I know there's only 1 path to reach step 1 as well. From there I can apply the same calculation we were using in our recursive strategy and store the results in an array.


  • Paths[0] → 1

  • Paths[1]→ 1

  • Paths[2]→ Paths[n-1] + Paths[n-2] = Paths[1] + Paths[0] = 1+1 = 2

  • Paths[3] → Paths[n-1] + Paths[n-2] = Paths[2] + Paths[1] = 2+1 = 3


This solution still has a time complexity of O(n), and we don't have to worry about stack depth as a limiting factor anymore.


Optimize space?

At this point we have a working solution with an optimal time complexity. But what about space complexity? With an array tracking every value of n, our space complexity is O(n).

Can we do better?


To answer that, let's recognize that at any particular moment, we actually only care about two values:

  • Paths to reach n - 1

  • Paths to reach n - 2

So what if instead of tracking all possible, we can get an O(1) space complexity by simply tracking these two variables!


5. Discuss Trade-Offs Between Your Solutions

Approach

Time

Space

Pros

Cons

Brute-force recursion

O(2ⁿ)

O(n)

Very intuitive

Too slow

Memoized recursion

O(n)

O(n)

Optimized time complexity

Recursive stack depth issues

Bottom-up DP array

O(n)

O(n)

Optimized time complexity, easy to reason through

Unnecessary space

Space-optimized DP

O(n)

O(1)

Optimized, Fast and clean

Slightly less obvious


6. Write Pseudocode to Structure Your Thoughts

if n <= 1: return 1

prev2 = 1
prev1 = 1

for i from 2 to n:
    curr = prev1 + prev2
    prev2 = prev1
    prev1 = curr

return prev1

7. Consider Edge Cases

  • n = 0

  • n = 1

  • Very large n

  • Confirm integer overflow assumptions

  • Negative inputs (not allowed)


8. Write Full Code Syntax (Java)

public class Solution {
    public int climbStairs(int n) {
        if (n <= 1) {
            return 1;
        }

        int prev2 = 1; // ways to reach step 0
        int prev1 = 1; // ways to reach step 1

        for (int i = 2; i <= n; i++) {
            int current = prev1 + prev2;
            prev2 = prev1;
            prev1 = current;
        }

        return prev1;
    }
}

9. Test Your Code

System.out.println(new Solution().climbStairs(0)); // 1
System.out.println(new Solution().climbStairs(1)); // 1
System.out.println(new Solution().climbStairs(2)); // 2
System.out.println(new Solution().climbStairs(3)); // 3
System.out.println(new Solution().climbStairs(5)); // 8

10. Key Lessons to Remember for Future Questions

  • Counting paths is a classic example of a dynamic programming problem

  • When recursion forces you to recompute the same values, dynamic programming should help you optimize

  • Many DP problems reduce to simple recurrences

  • Don't forget to consider space optimization! Space optimization often follows naturally once the recurrence is clear

Recent Posts

See All
House Robber

Learn how to solve the House Robber coding problem to prepare for your next technical interview!

 
 
 
Fibonacci Number

Learn how to solve the Fibonacci Number coding problem to prepare for your next technical interview!

 
 
 

Comments


Drop Me a Line, Let Me Know What You Think

Thanks for submitting!

© 2023 by Train of Thoughts. Proudly created with Wix.com

bottom of page