LeetCode 518:零钱兑换 II(Coin Change II)------ 题解 ✅
📖 内容概要
给定不同面额的硬币 coins 和一个总金额 amount,
计算 凑成总金额的组合数(每种硬币无限使用)。
✅ 完全背包
✅ 组合问题(不计顺序)
✅ 遍历顺序极其关键
💡 解题思路
一、问题本质
- 硬币数量无限
- 只关心 金额是否凑成
- 只统计 组合数(不计顺序)
👉 典型的 完全背包(组合计数)
二、DP 定义
java
dp[j] = 凑成金额 j 的组合数
三、状态转移方程
java
dp[j] += dp[j - coins[i]]
含义:
- 使用当前硬币
coins[i] - 新增的组合数 = 凑成
j - coins[i]的方法数
🔥 核心重点:遍历顺序
✅ 正确的遍历顺序(本题)
java
for (int i = 0; i < coins.length; i++) { // 物品
for (int j = coins[i]; j <= amount; j++) { // 背包
dp[j] += dp[j - coins[i]];
}
}
为什么这样写?
| 层级 | 含义 |
|---|---|
外层 i |
枚举硬币种类 |
内层 j |
枚举金额 |
✅ 保证硬币按顺序使用
✅ 不会出现 1+2 和 2+1 重复
✅ 得到的是 组合数
❌ 错误的遍历顺序
java
for (int j = 0; j <= amount; j++) {
for (int i = 0; i < coins.length; i++) {
dp[j] += dp[j - coins[i]];
}
}
❌ 会统计排列数
❌ 1+2 和 2+1 被视为不同方案
🔑 记忆口诀
求组合数:先物品,后背包
求排列数:先背包,后物品
✅ AC 代码(Java)
java
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1; // 凑成金额 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];
}
}
⏱️ 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(coins.length × amount) |
| 空间复杂度 | O(amount) |
✅ 一句话总结
完全背包 + 组合计数 = 外层硬币、内层金额,正序遍历。