Talentd
FleetCode
Make and follow sheets.Sign in

Coin Change II solution and explanation

MediumArrayDynamic Programming6 min read59.1% acceptanceUpdated 9 Apr 2026
Coin Change 2 - Dynamic Programming Unbounded Knapsack - Leetcode 518 - PythonNeetCode · 23:19 · 192K viewsWatch on YouTube
9 more videos on this problem
  1. Coin Change 2 - Dynamic Programming Unbounded Knapsack - Leetcode 518 - PythonNeetCode · 23:19 · 192K views
  2. Coin Change 2 | Dynamic programming | Leetcode #518Techdose · 20:32 · 58K views
  3. Coin Change II | Made Super Easy | Recursion | Memoization | AMAZON | Leetcode-518codestorywithMIK · 24:32 · 23K views
  4. [Java] Leetcode 518. Coin Change 2 [DP Distinct Ways #8]Eric Programming · 18:24 · 1K views
  5. 518. Coin Change II - LeetCode Python SolutionCode with Carter · 9:32 · 1K views
  6. Coin Change II | LeetCode 518 DP ExplainedDestination FAANG · 13:50 · 937 views
  7. Unlocking the Secrets of Dynamic Programming in Rust | LeetCode 518. Coin Change II - Live CodingvanAmsen · 6:14 · 491 views
  8. Coin Change II Problem Explained | Number of Ways with Dynamic Programming | LeetCode 518Baa Code Education · 3:04 · 403 views
  9. Daily Leetcode #1228: Aug 11, 2023 - 518. Coin Change IIProgramming Live with Larry · 13:43 · 393 views
  10. Total Unique Ways To Make Change - Dynamic Programming ("Coin Change 2" on LeetCode)Back To Back SWE · 11:42 · 180K views

The problem

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

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

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

The answer is guaranteed to fit into a signed 32-bit integer.

Example 1:

Input: amount = 5, coins = [1,2,5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Example 3:

Input: amount = 10, coins = [10]
Output: 1

Constraints:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • All the values of coins are unique.
  • 0 <= amount <= 5000

Approach overview

Problem Overview: Given a list of coin denominations and a target amount, compute how many unique combinations of coins can make that amount. Each coin can be used unlimited times, and combinations with different orderings count as the same result.

Approach 1: Dynamic Programming with 2D Array (O(n × amount) time, O(n × amount) space)

This approach models the problem similarly to an unbounded knapsack. Use a 2D DP table dp[i][a] where i represents the first i coin types and a represents the current amount. For each state, you either exclude the current coin (dp[i-1][a]) or include it (dp[i][a - coin]) because coins can be reused. Iterating through coins and amounts fills the table and ensures every combination is counted exactly once. This method is easy to reason about and helpful when learning dynamic programming transitions.

Approach 2: Dynamic Programming with 1D Array (O(n × amount) time, O(amount) space)

The optimized version collapses the 2D DP table into a single array dp[a] representing the number of ways to form amount a. Initialize dp[0] = 1 since there is one way to make zero amount. Iterate through each coin first, then iterate amounts from the coin value up to the target. Update using dp[a] += dp[a - coin]. Processing coins in the outer loop prevents counting permutations like [1,2] and [2,1] separately, which is a key detail in combination problems involving arrays. This version reduces memory usage significantly while keeping the same time complexity.

Recommended for interviews: The 1D dynamic programming approach is the expected solution. It demonstrates strong understanding of dynamic programming state transitions and space optimization. Starting with the 2D formulation shows clear reasoning about the recurrence, while reducing it to 1D shows practical optimization skills interviewers look for.

Approach 1: Dynamic Programming with 2D Array

This method involves using a 2D array to keep track of number of ways to make up a certain amount using coins up to a particular denomination. The rows in the array represent the number of coins available (up to that index), and the columns represent each possible amount up to the target.

Define dp[i][j] as the number of ways to make amount j using the first i types of coins. Initialize dp[i][0] to 1 since there is one way to make amount 0 (choosing no coin). For each coin, iterate over all amounts and accumulate the number of ways by including the current coin.

Time complexity is O(n*m) where n is the amount and m is the number of coins. Space complexity is also O(n*m).

def change(amount, coins):
    dp = [[0] * (amount + 1) for _ in range(len(coins) + 1)]
    for i in range(len(coins) + 1):
        dp[i][0] = 1
    for i in range(1, len(coins) + 1):
        for j in range(1, amount + 1):
            dp[i][j] = dp[i-1][j]
            if j >= coins[i-1]:
                dp[i][j] += dp[i][j-coins[i-1]]
    return dp[len(coins)][amount]

print(change(5, [1, 2, 5]))  # Output: 4
import java.util.Arrays;

public class CoinChange {
    public int change(int amount, int[] coins) {
        int[][] dp = new int[coins.length + 1][amount + 1];
        for (int i = 0; i <= coins.length; i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i <= coins.length; i++) {
            for (int j = 1; j <= amount; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= coins[i - 1]) {
                    dp[i][j] += dp[i][j - coins[i - 1]];
                }
            }
        }
        return dp[coins.length][amount];
    }

    public static void main(String[] args) {
        CoinChange cc = new CoinChange();
        int[] coins = {1, 2, 5};
        int amount = 5;
        System.out.println("Number of combinations: " + cc.change(amount, coins));
    }
}
#include <vector>
#include <iostream>
using namespace std;

int change(int amount, vector<int>& coins) {
    vector<vector<int>> dp(coins.size() + 1, vector<int>(amount + 1, 0));
    for (int i = 0; i <= coins.size(); i++) {
        dp[i][0] = 1;
    }
    for (size_t i = 1; i <= coins.size(); i++) {
        for (int j = 1; j <= amount; j++) {
            dp[i][j] = dp[i-1][j];
            if (j >= coins[i-1]) {
                dp[i][j] += dp[i][j - coins[i-1]];
            }
        }
    }
    return dp[coins.size()][amount];
}

int main() {
    vector<int> coins = {1, 2, 5};
    int amount = 5;
    cout << "Number of combinations: " << change(amount, coins) << endl;
    return 0;
}
function change(amount, coins) {
    let dp = Array(coins.length + 1).fill(0).map(() => Array(amount + 1).fill(0));
    for (let i = 0; i <= coins.length; i++) {
        dp[i][0] = 1;
    }
    for (let i = 1; i <= coins.length; i++) {
        for (let j = 1; j <= amount; j++) {
            dp[i][j] = dp[i-1][j];
            if (j >= coins[i-1]) {
                dp[i][j] += dp[i][j-coins[i-1]];
            }
        }
    }
    return dp[coins.length][amount];
}

console.log(change(5, [1, 2, 5])); // Output: 4
#include <stdio.h>

int change(int amount, int* coins, int coinsSize) {
    int dp[coinsSize + 1][amount + 1];
    for (int i = 0; i <= coinsSize; i++) {
        dp[i][0] = 1;
    }
    for (int j = 1; j <= amount; j++) {
        dp[0][j] = 0;
    }
    for (int i = 1; i <= coinsSize; i++) {
        for (int j = 1; j <= amount; j++) {
            dp[i][j] = dp[i - 1][j];
            if (j >= coins[i - 1]) {
                dp[i][j] += dp[i][j - coins[i - 1]];
            }
        }
    }
    return dp[coinsSize][amount];
}

int main() {
    int coins[] = {1, 2, 5};
    int amount = 5;
    printf("Number of combinations: %d\n", change(amount, coins, 3));
    return 0;
}
using System;

public class CoinChange {
    public int Change(int amount, int[] coins) {
        int[,] dp = new int[coins.Length + 1, amount + 1];
        for (int i = 0; i <= coins.Length; i++) {
            dp[i, 0] = 1;
        }
        for (int i = 1; i <= coins.Length; i++) {
            for (int j = 1; j <= amount; j++) {
                dp[i, j] = dp[i - 1, j];
                if (j >= coins[i - 1]) {
                    dp[i, j] += dp[i, j - coins[i - 1]];
                }
            }
        }
        return dp[coins.Length, amount];
    }

    public static void Main(string[] args) {
        CoinChange cc = new CoinChange();
        int[] coins = { 1, 2, 5 };
        int amount = 5;
        Console.WriteLine("Number of combinations: " + cc.Change(amount, coins));
    }
}

In Python, the 2D list `dp` allows us to handle subproblem results for dynamic programming. We consider each coin and all amounts achievable with prior denominations, accumulating solutions from previously calculated states.

Complexity

Time Complexity: O(n*m), Space Complexity: O(n*m) where `n` is the `amount` and `m` is the number of coins.

Try this approach in the editor

Approach 2: Dynamic Programming with 1D Array

This method optimizes space usage by employing a 1D array to store results. It iterates over each coin and updates the number of ways for each possible amount.

Define dp[j] as the number of ways to make amount j. Initially, set dp[0] to 1 (to denote the empty set that sums up to zero). Iterate through each coin, then iterate over possible amounts in nested for-loops from current coin's value to amount, updating the dp array by adding ways from previous calculations.

Time complexity remains O(n*m), but space complexity improves to O(n).

def change(amount, coins):
    dp = [0] * (amount + 1)
    dp[0] = 1
    for coin in coins:
        for j in range(coin, amount + 1):
            dp[j] += dp[j - coin]
    return dp[amount]

print(change(5, [1, 2, 5]))  # Output: 4
import java.util.Arrays;

public class CoinChange {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int i = 0; i < coins.length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }

    public static void main(String[] args) {
        CoinChange cc = new CoinChange();
        int[] coins = {1, 2, 5};
        int amount = 5;
        System.out.println("Number of combinations: " + cc.change(amount, coins));
    }
}
#include <vector>
#include <iostream>
using namespace std;

int change(int amount, vector<int>& coins) {
    vector<int> dp(amount + 1, 0);
    dp[0] = 1;
    for (size_t i = 0; i < coins.size(); i++) {
        for (int j = coins[i]; j <= amount; j++) {
            dp[j] += dp[j - coins[i]];
        }
    }
    return dp[amount];
}

int main() {
    vector<int> coins = {1, 2, 5};
    int amount = 5;
    cout << "Number of combinations: " << change(amount, coins) << endl;
    return 0;
}
function change(amount, coins) {
    let dp = Array(amount + 1).fill(0);
    dp[0] = 1;
    for (let coin of coins) {
        for (let j = coin; j <= amount; j++) {
            dp[j] += dp[j - coin];
        }
    }
    return dp[amount];
}

console.log(change(5, [1, 2, 5])); // Output: 4
#include <stdio.h>

int change(int amount, int* coins, int coinsSize) {
    int dp[amount + 1];
    for (int i = 0; i <= amount; i++) dp[i] = 0;
    dp[0] = 1;
    for (int i = 0; i < coinsSize; i++) {
        for (int j = coins[i]; j <= amount; j++) {
            dp[j] += dp[j - coins[i]];
        }
    }
    return dp[amount];
}

int main() {
    int coins[] = {1, 2, 5};
    int amount = 5;
    printf("Number of combinations: %d\n", change(amount, coins, 3));
    return 0;
}
using System;

public class CoinChange {
    public int Change(int amount, int[] coins) {
        int[] dp = new int[amount + 1];
        dp[0] = 1;
        for (int i = 0; i < coins.Length; i++) {
            for (int j = coins[i]; j <= amount; j++) {
                dp[j] += dp[j - coins[i]];
            }
        }
        return dp[amount];
    }

    public static void Main(string[] args) {
        CoinChange cc = new CoinChange();
        int[] coins = { 1, 2, 5 };
        int amount = 5;
        Console.WriteLine("Number of combinations: " + cc.Change(amount, coins));
    }
}

Python reduces space use to a 1-dimensional array `dp`, fully opting in to utilize prior solutions by adding coin contributions directly into new entries without need for a secondary dimension.

Complexity

Time Complexity: O(n*m), Space Complexity: O(n) where `n` is the `amount` and `m` is the number of coins.

Try this approach in the editor

Approach 3: Dynamic Programming (Complete Knapsack)

We define f[i][j] as the number of coin combinations to make up the amount j using the first i types of coins. Initially, f[0][0] = 1, and the values of other positions are all 0.

We can enumerate the quantity k of the last coin used, then we have equation one:

$ f[i][j] = f[i - 1][j] + f[i - 1][j - x] + f[i - 1][j - 2 times x] + cdots + f[i - 1][j - k times x]

where x represents the face value of the i-th type of coin.

Let j = j - x, then we have equation two:

f[i][j - x] = f[i - 1][j - x] + f[i - 1][j - 2 times x] + cdots + f[i - 1][j - k times x]

Substituting equation two into equation one, we can get the following state transition equation:

f[i][j] = f[i - 1][j] + f[i][j - x]

The final answer is f[m][n].

The time complexity is O(m times n), and the space complexity is O(m times n). Where m and n$ are the number of types of coins and the total amount, respectively.

function change(amount: number, coins: number[]): number {
    const [m, n] = [coins.length, amount];
    const f: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
    f[0][0] = 1;
    for (let i = 1; i <= m; ++i) {
        for (let j = 0; j <= n; ++j) {
            f[i][j] = f[i - 1][j];
            if (j >= coins[i - 1]) {
                f[i][j] += f[i][j - coins[i - 1]];
            }
        }
    }
    return f[m][n];
}
func change(amount int, coins []int) int {
	m, n := len(coins), amount
	f := make([][]int, m+1)
	for i := range f {
		f[i] = make([]int, n+1)
	}
	f[0][0] = 1
	for i := 1; i <= m; i++ {
		for j := 0; j <= n; j++ {
			f[i][j] = f[i-1][j]
			if j >= coins[i-1] {
				f[i][j] += f[i][j-coins[i-1]]
			}
		}
	}
	return f[m][n]
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming with 2D Array

Time Complexity: O(n*m), Space Complexity: O(n*m) where `n` is the `amount` and `m` is the number of coins.

Dynamic Programming with 1D Array

Time Complexity: O(n*m), Space Complexity: O(n) where `n` is the `amount` and `m` is the number of coins.

Dynamic Programming (Complete Knapsack)–

Complexity analysis

ApproachTimeSpaceWhen to Use
Dynamic Programming with 2D ArrayO(n × amount)O(n × amount)Best for understanding the DP state transition and building intuition for combination counting problems.
Dynamic Programming with 1D ArrayO(n × amount)O(amount)Preferred solution when memory optimization matters and the standard approach expected in coding interviews.

Frequently asked questions

Is Coin Change II easy or hard?

Coin Change II is considered a Medium difficulty problem. The challenge is recognizing it as a combination-counting dynamic programming problem and avoiding permutation counting by iterating coins before amounts.

What is the best approach for Coin Change II?

The best approach uses dynamic programming with a 1D array. Iterate through each coin and update a DP array where dp[a] stores the number of ways to form amount a. The algorithm runs in O(n × amount) time and O(amount) space, which is optimal for this problem.

How to solve Coin Change II in O(n × amount)?

Use dynamic programming. Initialize dp[0] = 1 and iterate through each coin. For every amount from coin value to target, update dp[a] += dp[a - coin]. This counts combinations while preventing permutation duplicates.

What data structure is used in Coin Change II?

The core data structure is a dynamic programming array. Either a 2D table dp[i][amount] or a space‑optimized 1D array dp[amount] is used to store the number of ways to build each amount using available coins.

What is the time complexity of Coin Change II?

The standard dynamic programming solution runs in O(n × amount) time, where n is the number of coin types and amount is the target value. Each coin iterates through all amounts once, updating the number of combinations.

Coin Change II Python or Java solution approach?

Python and Java implementations typically use the 1D DP approach. Initialize an array dp of size amount + 1, set dp[0] = 1, then iterate through each coin and update dp[a] += dp[a - coin] for valid amounts.

Is Coin Change II asked at Google, Amazon, or Meta?

Coin Change II appears frequently in coding interviews at companies like Amazon, Google, and Meta because it tests dynamic programming fundamentals, especially the unbounded knapsack pattern and DP state transitions.

Comments

Now write it yourselfClose this page and solve Coin Change II in the editor, without looking.
Practise Coin Change II