Skip to content

746. Min Cost Climbing Stairs (Easy)

Problem

Given an array cost where cost[i] is the cost of step i, you can start at step 0 or step 1. Each move climbs 1 or 2 steps. Return the minimum total cost to reach just past the last step.

Example

  • cost = [10, 15, 20]15 (start at 1, pay 15, step over the top)
  • cost = [1,100,1,1,1,100,1,1,100,1]6

LeetCode 746 · Link · Easy

Try it yourself

idle

Starter code: this editor begins with intentional TODOs. Fill the function, run the embedded tests, then compare your solution with the worked approaches below.

Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 1: Recursive, min cost from step i

min_from(i) = cost[i] + min(min_from(i+1), min_from(i+2)). Return min(min_from(0), min_from(1)).

def min_cost_climbing_stairs(cost):
n = len(cost)
def min_from(i):
if i >= n: # L1: base case, past top
return 0
return cost[i] + min(min_from(i + 1), min_from(i + 2)) # L2: two recursive branches
return min(min_from(0), min_from(1)) # L3: best of starting at 0 or 1

Where the time goes, line by line

Variables: n = len(cost).

LinePer-call costTimes executedContribution
L1 (base case)O(1)O(1)O(2n)O(2ⁿ) leavesO(2n)O(2ⁿ)
L2 (two recursive calls)O(1)O(1) + subtreeO(2n)O(2ⁿ) nodesO(2n)O(2ⁿ) ← dominates
L3 (initial call)O(1)O(1)1O(1)O(1)

Each call spawns two more; the tree doubles in width at every level, giving an exponential node count.

Complexity

  • Time: O(2n)O(2ⁿ), driven by L2 (exponential branching, no caching).
  • Space: O(n)O(n) recursion stack.

Approach 2: Memoized recursion

from functools import lru_cache
def min_cost_climbing_stairs(cost):
n = len(cost)
@lru_cache(maxsize=None)
def f(i):
if i >= n: # L1: base case
return 0
return cost[i] + min(f(i + 1), f(i + 2)) # L2: two sub-calls, cached after first hit
return min(f(0), f(1)) # L3: best start

Where the time goes, line by line

Variables: n = len(cost).

LinePer-call costTimes executedContribution
L1 (base case)O(1)O(1)2O(1)O(1)
L2 (cached sub-calls)O(1)O(1) eachn unique statesO(n)O(n) ← dominates
L3 (initial call)O(1)O(1)1O(1)O(1)

Each of the n steps is computed exactly once; subsequent calls hit the cache in O(1)O(1).

Complexity

  • Time: O(n)O(n), driven by L2 (n unique states, each computed once).
  • Space: O(n)O(n) cache + stack.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 3: Bottom-up DP array

dp[i] = min cost to step on stair i. Build iteratively from left to right.

def min_cost_climbing_stairs(cost):
n = len(cost)
dp = [0] * n # L1: O(n) allocation
dp[0], dp[1] = cost[0], cost[1] # L2: seed base cases
for i in range(2, n): # L3: loop n-2 times
dp[i] = cost[i] + min(dp[i-1], dp[i-2]) # L4: O(1) recurrence
return min(dp[n-1], dp[n-2]) # L5: O(1) return

Where the time goes, line by line

Variables: n = len(cost).

LinePer-call costTimes executedContribution
L1 (allocate dp)O(n)O(n)1O(n)O(n)
L2 (seed)O(1)O(1)1O(1)O(1)
L3, L4 (loop + recurrence)O(1)O(1)n - 2O(n)O(n) ← dominates
L5 (return)O(1)O(1)1O(1)O(1)

Complexity

  • Time: O(n)O(n), driven by L3/L4 (n - 2 iterations).
  • Space: O(n)O(n) for the dp array.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Approach 4: Bottom-up with two variables (optimal)

dp[i] = min cost to stand on step i. Then dp[i] = cost[i] + min(dp[i-1], dp[i-2]). Answer = min(dp[n-1], dp[n-2]).

def min_cost_climbing_stairs(cost):
n = len(cost) # L1: get length
a, b = cost[0], cost[1] # L2: seed dp[0], dp[1]
for i in range(2, n): # L3: loop n-2 times
a, b = b, cost[i] + min(a, b) # L4: O(1) update per step
return min(a, b) # L5: best of last two steps

Where the time goes, line by line

Variables: n = len(cost).

LinePer-call costTimes executedContribution
L1 (length)O(1)O(1)1O(1)O(1)
L2 (init)O(1)O(1)1O(1)O(1)
L3 (loop)O(1)O(1)n - 2O(n)O(n) ← dominates
L4 (update)O(1)O(1)n - 2O(n)O(n)
L5 (final min)O(1)O(1)1O(1)O(1)

The loop runs exactly n - 2 times, each iteration is a constant-cost update. No allocation beyond two scalars.

Complexity

  • Time: O(n)O(n), driven by L3/L4.
  • Space: O(1)O(1), just two scalars.

Try this approach:

idle
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).

Summary

ApproachTimeSpace
Naive recursionO(2n)O(2ⁿ)O(n)O(n)
MemoizedO(n)O(n)O(n)O(n)
Bottom-up, dp arrayO(n)O(n)O(n)O(n)
Bottom-up, two varsO(n)O(n)O(1)O(1)

Test cases

# Quick smoke tests, paste into a REPL or save as test_746.py and run.
# Uses the canonical implementation (Approach 4: bottom-up two variables).
def min_cost_climbing_stairs(cost):
n = len(cost)
a, b = cost[0], cost[1]
for i in range(2, n):
a, b = b, cost[i] + min(a, b)
return min(a, b)
def _run_tests():
assert min_cost_climbing_stairs([10, 15, 20]) == 15 # LeetCode example 1
assert min_cost_climbing_stairs([1,100,1,1,1,100,1,1,100,1]) == 6 # LeetCode example 2
assert min_cost_climbing_stairs([0, 0]) == 0 # all zeros
assert min_cost_climbing_stairs([1, 2]) == 1 # two steps, pick cheaper
assert min_cost_climbing_stairs([5, 3, 1, 2]) == 4 # skip alternating
print("all tests pass")
if __name__ == "__main__":
_run_tests()
  • Arrays, input; implicit DP array
  • State Compression, the memory reduction tactic for keeping only states the next transition needs.
  • Dynamic Programming, the state and transition model for reusing answers to overlapping subproblems.