Skip to content

Coin Change

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

coinsamountOutputExplanation
[1,2,5]51One 5-coin makes 5
[2]3-1Cannot make 3 with only 2-coins
[10]101One 10-coin makes 10
[1,3,4]62Two 3-coins or one 3-coin and three 1-coins
  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 2^31 - 1
  • 0 <= amount <= 10^4
ApproachTimeSpaceBest For
DPO(n * amount)O(amount)Classic approach, intuitive logic
BFSO(n * amount)O(amount)Shortest path perspective, level-based traversal

where n = len(coins)

  • Pick DP if you want the fundamental dynamic programming approach.
  • Pick BFS if you prefer thinking in terms of graph/shortest path problems.
Classic DP
DP Array
O(n * amount) time · O(amount) space
Graph Perspective
BFS
O(n * amount) time · O(amount) space
★ Recommended

dp[i] = minimum coins needed to make amount i.

For each amount i, try all coins and pick the one that results in the minimum number of coins.

⏱ Time O(n * amount) Double loop 💾 Space O(amount) DP array

Input: coins = [1, 3, 4], amount = 6

amountcoin=1coin=3coin=4dp[amount]
00
11+dp[0]=1N/AN/A1
21+dp[1]=2N/AN/A2
31+dp[2]=31+dp[0]=1N/A1
41+dp[3]=21+dp[1]=21+dp[0]=11
51+dp[4]=21+dp[2]=31+dp[1]=22
61+dp[5]=31+dp[3]=21+dp[2]=32

Result: dp[6] = 2 (two 3-coins)

function coinChangeDP(coins, amount):
dp = array of size amount + 1, all initialized to infinity
dp[0] = 0
for i from 1 to amount:
for coin in coins:
if coin <= i and dp[i - coin] is not infinity:
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] is not infinity else -1
coin_change_dp_array.py
from typing import List
def coin_change_dp_array(coins: List[int], amount: int) -> int:
"""
Find minimum number of coins needed to make amount using DP array.
Time Complexity: O(n * amount) where n = len(coins)
Space Complexity: O(amount)
dp[i] = minimum coins needed to make amount i
For each amount, try all coins and take the minimum
dp[i] = min(dp[i - coin] + 1) for all coins <= i
"""
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for i in range(1, amount + 1):
for coin in coins:
if coin <= i:
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
if __name__ == "__main__":
print(coin_change_dp_array([1, 2, 5], 5)) # 1 (one 5-coin)
print(coin_change_dp_array([2], 3)) # -1 (impossible)
print(coin_change_dp_array([10], 10)) # 1 (one 10-coin)
print(coin_change_dp_array([1, 3, 4], 6)) # 2 (two 3-coins or one 3-coin + three 1-coins)
🎯 Interview Favourite

Treat this as a shortest path problem: start at amount and work backward, trying each coin denomination. The first time we reach 0, we’ve found the minimum coins.

BFS explores level by level, where level k contains all amounts reachable with k coins.

⏱ Time O(n * amount) Queue and visited set 💾 Space O(amount) Visited set

BFS guarantees finding the shortest path (minimum coins) because it explores all possibilities at distance k before exploring at distance k+1.

function coinChangeBFS(coins, amount):
if amount == 0:
return 0
queue = [(amount, 0)] # (remaining_amount, num_coins)
visited = {amount}
while queue is not empty:
curr_amount, num_coins = queue.pop_front()
for coin in coins:
next_amount = curr_amount - coin
if next_amount == 0:
return num_coins + 1
if next_amount > 0 and next_amount not in visited:
visited.add(next_amount)
queue.push_back((next_amount, num_coins + 1))
return -1
coin_change_bfs.py
from typing import List
from collections import deque
def coin_change_bfs(coins: List[int], amount: int) -> int:
"""
Find minimum number of coins needed to make amount using BFS.
Time Complexity: O(n * amount) where n = len(coins)
Space Complexity: O(amount)
Treat this as a shortest path problem in a graph:
- Start at amount 0 (need 0 coins)
- Each coin represents an edge to a smaller amount
- BFS finds the shortest path to amount
"""
if amount == 0:
return 0
queue = deque([(amount, 0)]) # (remaining_amount, num_coins)
visited = {amount}
while queue:
current_amount, num_coins = queue.popleft()
for coin in coins:
next_amount = current_amount - coin
if next_amount == 0:
return num_coins + 1
if next_amount > 0 and next_amount not in visited:
visited.add(next_amount)
queue.append((next_amount, num_coins + 1))
return -1
if __name__ == "__main__":
print(coin_change_bfs([1, 2, 5], 5)) # 1
print(coin_change_bfs([2], 3)) # -1
print(coin_change_bfs([10], 10)) # 1
print(coin_change_bfs([1, 3, 4], 6)) # 2
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
coin_change_space_optimized.py
def solution(): return 0