You are given an integer n representing the number of steps to reach the top of a staircase. You can climb with either 1 or 2 steps at a time.
Return the number of distinct ways to climb to the top of the staircase.
Example 1:
Input: n = 2
Output: 2Explanation:
1 + 1 = 22 = 2Example 2:
Input: n = 3
Output: 3Explanation:
1 + 1 + 1 = 31 + 2 = 32 + 1 = 3Constraints:
1 <= n <= 30
You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps.
At each step, we have two choices: climb one step or climb two steps. We can solve this by considering both options and picking the minimum using recursion. However, this results in O(2^n) time complexity. Can you think of a better approach? Perhaps, try to avoid the repeated work of calling recursion more than once with same parameters.
This is a Dynamic Programming problem. We can use Memoization to avoid repeated work. Create an n-sized array cache to store the results of recursive calls. When the recursion is called with specific parameters, return the stored value if it has already been computed. How would you implement this?
We start the initial recursion with i = 0, indicating that we are at position i. We first check if the current recursion with the given i is already cached. If it is, we immediately return the stored value. Otherwise, we perform the recursion, store the result in the cache, and then return it. Can you think of the base condition to stop the recursion?
At each recursion, we perform two recursive calls: one for climbing one step and another for climbing two steps. The minimum return value between the two is the result for the current recursion. The base condition is to return 0 if i == n. This is a one-dimensional dynamic programming problem, which can be further optimized using more advanced techniques.
Before attempting this problem, you should be comfortable with:
At every step, you have two choices:
So from any step i, the number of ways to reach the top is:
i + 1i + 2This naturally forms a binary recursion tree where we try all possible paths.
n, that path counts as 1 valid wayn, it’s an invalid pathThis is a classic example of exploring all possibilities using recursion.
0.dfs(i):i == n, return 1 (one valid way).i > n, return 0 (invalid path).dfs(i + 1) → take 1 stepdfs(i + 2) → take 2 stepsdfs(0).This is the optimized version of the recursive solution.
The key observation is:
i is always the same whenever we reach i.So instead of recomputing, we store the result the first time we compute it and reuse it later.
This is exactly what Top-Down Dynamic Programming (Memoization) does.
We still think recursively, but we avoid redundant work.
cache of size n initialized with -1.dfs(i):i == n, return 1 (valid way).i > n, return 0 (invalid path).cache[i] != -1, return the cached value.dfs(i + 1) + dfs(i + 2)cache[i]dfs(0) as the final answer.To reach step i, you can only come from:
i - 1 (1 step)i - 2 (2 steps)So the total ways to reach step i is the sum of ways to reach the previous two steps.
This forms a Fibonacci-like pattern.
n <= 2, return n.dp[i] = number of ways to reach step i.dp[1] = 1dp[2] = 2i from 3 to n:dp[i] = dp[i - 1] + dp[i - 2]dp[n].At any step, the number of ways depends only on the previous two steps.
So instead of storing all values in a DP array, we can just keep two variables that represent:
This is the same Fibonacci idea, but optimized to use constant space.
one -> ways to reach the current steptwo -> ways to reach the previous step1 (base case).n - 1 times:one + twoone.The number of ways to climb stairs follows the Fibonacci sequence:
n, you can come from n-1 or n-2ways(n) = ways(n-1) + ways(n-2)Fibonacci numbers can be computed efficiently using matrix exponentiation, which reduces the time from linear to logarithmic.
|1 1|
|1 0|n using binary exponentiation.[0][0] of the final matrix is the answer.class Solution:
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
def matrix_mult(A, B):
return [[A[0][0] * B[0][0] + A[0][1] * B[1][0],
A[0][0] * B[0][1] + A[0][1] * B[1][1]],
[A[1][0] * B[0][0] + A[1][1] * B[1][0],
A[1][0] * B[0][1] + A[1][1] * B[1][1]]]
def matrix_pow(M, p):
result = [[1, 0], [0, 1]]
base = M
while p:
if p % 2 == 1:
result = matrix_mult(result, base)
base = matrix_mult(base, base)
p //= 2
return result
M = [[1, 1], [1, 0]]
result = matrix_pow(M, n)
return result[0][0]The number of ways to climb stairs follows the Fibonacci sequence.
There is a closed-form mathematical formula (called Binet’s Formula) that directly computes the nth Fibonacci number using powers and square roots, without loops or recursion.
This works because Fibonacci numbers can be expressed using two constants derived from the golden ratio.
φ = (1 + √5) / 2 and its conjugate ψ = (1 − √5) / 2.Fib(n+1), adjust n accordingly.Initializing DP incorrectly or misunderstanding what dp[0] represents. For this problem, dp[1] = 1 (one way to climb 1 step) and dp[2] = 2 (two ways to climb 2 steps). Starting the loop from index 1 instead of 3 overwrites these base cases.
# Wrong: loop starts too early
for i in range(1, n + 1):
dp[i] = dp[i-1] + dp[i-2]
# Correct: preserve base cases
for i in range(3, n + 1):
dp[i] = dp[i-1] + dp[i-2]Attempting to access dp[n-1] or dp[n-2] when n is 1 or 2 without proper base case handling, causing index out of bounds errors. Always check if n <= 2: return n before running the general loop.