103 lines
3.1 KiB
YAML
103 lines
3.1 KiB
YAML
title: Climbing Stairs
|
|
slug: climbing-stairs
|
|
difficulty: easy
|
|
leetcode_id: 70
|
|
leetcode_url: https://leetcode.com/problems/climbing-stairs/
|
|
categories:
|
|
- dynamic-programming
|
|
- math
|
|
patterns:
|
|
- dynamic-programming
|
|
|
|
description: |
|
|
You are climbing a staircase. It takes n steps to reach the top.
|
|
|
|
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
|
|
|
|
constraints: |
|
|
- 1 <= n <= 45
|
|
|
|
examples:
|
|
- input: "n = 2"
|
|
output: "2"
|
|
explanation: "Two ways: (1+1) or (2)"
|
|
- input: "n = 3"
|
|
output: "3"
|
|
explanation: "Three ways: (1+1+1), (1+2), (2+1)"
|
|
|
|
explanation:
|
|
approach: |
|
|
1. Recognize this follows the Fibonacci pattern
|
|
2. ways(n) = ways(n-1) + ways(n-2)
|
|
3. From step n-1, we can take 1 step to reach n
|
|
4. From step n-2, we can take 2 steps to reach n
|
|
5. Base cases: ways(1) = 1, ways(2) = 2
|
|
|
|
intuition: |
|
|
At any step, you either got there by taking 1 step from the previous position
|
|
or 2 steps from two positions back. This gives us the recurrence relation.
|
|
|
|
This is essentially the Fibonacci sequence! The number of ways to reach step n
|
|
equals the sum of ways to reach steps n-1 and n-2.
|
|
|
|
common_pitfalls:
|
|
- title: Using recursion without memoization
|
|
description: |
|
|
Naive recursion recalculates the same subproblems repeatedly, leading to
|
|
exponential time complexity. Either use memoization or iterative DP.
|
|
wrong_approach: "return climb(n-1) + climb(n-2) without caching"
|
|
correct_approach: "Use bottom-up DP or memoize recursive calls"
|
|
|
|
- title: Off-by-one in base cases
|
|
description: |
|
|
Carefully define base cases. There's 1 way to stay at ground (step 0),
|
|
1 way to reach step 1, and 2 ways to reach step 2.
|
|
|
|
key_takeaways:
|
|
- Many counting problems follow Fibonacci-like patterns
|
|
- Convert recursion to iteration for O(1) space
|
|
- Bottom-up DP avoids stack overflow for large inputs
|
|
- Recognize overlapping subproblems as a DP signal
|
|
|
|
time_complexity: "O(n)"
|
|
space_complexity: "O(1)"
|
|
complexity_explanation: |
|
|
Time: We compute n states, each in O(1).
|
|
Space: Only track two previous values (space-optimized DP).
|
|
|
|
solutions:
|
|
- approach_name: Space-Optimized DP (Optimal)
|
|
is_optimal: true
|
|
code: |
|
|
def climb_stairs(n: int) -> int:
|
|
if n <= 2:
|
|
return n
|
|
|
|
prev1, prev2 = 2, 1
|
|
for i in range(3, n + 1):
|
|
current = prev1 + prev2
|
|
prev2 = prev1
|
|
prev1 = current
|
|
|
|
return prev1
|
|
explanation: |
|
|
Track only the two previous values since that's all we need.
|
|
Equivalent to computing the nth Fibonacci number.
|
|
|
|
- approach_name: Recursive with Memoization
|
|
is_optimal: false
|
|
code: |
|
|
from functools import lru_cache
|
|
|
|
def climb_stairs(n: int) -> int:
|
|
@lru_cache(maxsize=None)
|
|
def dp(step: int) -> int:
|
|
if step <= 2:
|
|
return step
|
|
return dp(step - 1) + dp(step - 2)
|
|
|
|
return dp(n)
|
|
explanation: |
|
|
Top-down approach with memoization.
|
|
Uses O(n) space for the cache and recursion stack.
|