LC 39. 组合总和
题目描述
这是 LeetCode 上的 39. 组合总和 ,难度为 中等。
给定一个无重复元素的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明:
- 所有数字(包括
target
)都是正整数。 - 解集不能包含重复的组合。
示例 1:1
2
3
4
5
6
7输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:1
2
3
4
5
6
7
8输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:
- $1 <= candidates.length <= 30$
- $1 <= candidates[i] <= 200$
candidate
中的每个元素都是独一无二的。- $1 <= target <= 500$
DFS
这道题很明显就是在考察回溯算法。
之前跟你分享过的 37. 解数独(困难) 里面有提到我们应该如何快速判断一道题是否应该使用 DFS + 回溯算法来爆搜。
总的来说,你可以从两个方面来考虑:
1. 求的是所有的方案,而不是方案数。 由于求的是所有方案,不可能有什么特别的优化,我们只能进行枚举。这时候可能的解法有动态规划、记忆化搜索、DFS + 回溯算法。
2. 通常数据范围不会太大,只有几十。 如果是动态规划或是记忆化搜索的题的话,由于它们的特点在于低重复/不重复枚举,所以一般数据范围可以出到 $10^4$ 到 $10^5$,而 DFS + 回溯的话,通常会限制在 $30$ 以内。
这道题数据范围是 $30$ 以内,而且是求所有方案,因此我们使用 DFS + 回溯来求解。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32class Solution {
public List<List<Integer>> combinationSum(int[] cs, int t) {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> cur = new ArrayList<>();
dfs(cs, t, 0, ans, cur);
return ans;
}
/**
* cs: 原数组,从该数组进行选数
* t: 还剩多少值需要凑成。起始值为 target ,代表还没选择任何数;当 t = 0,代表选择的数凑成了 target
* u: 当前决策到 cs[] 中的第几位
* ans: 最终结果集
* cur: 当前结果集
*/
void dfs(int[] cs, int t, int u, List<List<Integer>> ans, List<Integer> cur) {
if (t == 0) {
ans.add(new ArrayList<>(cur));
return;
}
if (u == cs.length || t < 0) return;
// 枚举 cs[u] 的使用次数
for (int i = 0; cs[u] * i <= t; i++) {
dfs(cs, t - cs[u] * i, u + 1, ans, cur);
cur.add(cs[u]);
}
// 进行回溯。注意回溯总是将数组的最后一位弹出
for (int i = 0; cs[u] * i <= t; i++) {
cur.remove(cur.size() - 1);
}
}
}
- 时间复杂度:爆搜通常是指数级别的复杂度
- 空间复杂度:爆搜通常是指数级别的复杂度
最后
这是我们「刷穿 LeetCode」系列文章的第 No.39
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!