题目
- 链接:
- 题目描述:
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
注意:解集不能包含重复的组合。
- 输入
candidates = [10,1,2,7,6,1,5]
target = 8
- 输出
[[1,1,6],[1,2,5],[1,7],[2,6]]
思路
这道题就三个要求
- 在数组中找出和为
target
的组合; - 数组中每个数字只能用一次;
- 结果集合不能重复,也就是要去重。
刚开始的思路
- dfs + 回溯 + 剪大枝 求结果集合;
回溯:每个数字只能用一次,所有要回溯,虽然有重复的数字。
剪大枝:如果当前结果已经超出了target
,而数组是有序的,那么就说明后面的也超出了target
。这里 剪大枝 也只是为了优化。
- 去重。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
// dfs搜索 + 回溯
Arrays.sort(candidates);
res = dfs(candidates,target,0,list,res);
// 去重
int i = 0;
while (i < res.size()) {
boolean success = false;
for (int j = i + 1; j < res.size(); j++) {
if (isSame(res.get(i), res.get(j))) {
res.remove(j);
success = true;
}
}
if (success) {
if (i > 1) {
i--;
}else {
i = 0;
}
}else {
i++;
}
}
// 结果
return res;
}
/**
搜索
*/
public List<List<Integer>> dfs(int[] candidates,int target,int index,List<Integer> list, List<List<Integer>> res) {
if (target == 0) {
res.add(new ArrayList<>(list));
return res;
}
for (int i = index; i < candidates.length;i++) {
// 剪大枝
if (target < 0) {
break;
}
list.add(candidates[i]);
dfs(candidates,target-candidates[i],i+1,list,res);
// 回溯 因为每个数字只能用一次
list.remove(list.size()-1);
}
return res;
}
/**
去重
*/
public boolean isSame(List<Integer> list1, List<Integer> list2) {
int i = 0,j = 0;
while (i < list1.size() && j < list2.size()) {
if (list1.get(i) != list2.get(j)) {
return false;
}
i++;
j++;
}
return i == list1.size() && j == list2.size();
}
}
然后就超时了,确实时间复杂度挺高的。 于是看了一下解析,大佬们都是再在 递归 的时候去重的,也就是所谓的 剪枝,但是这里是 剪小枝。
代码
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> list = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
// dfs搜索 + 回溯
Arrays.sort(candidates);
res = dfs(candidates,target,0,list,res);
// 结果
return res;
}
/**
搜索
*/
public List<List<Integer>> dfs(int[] candidates,int target,int index,List<Integer> list, List<List<Integer>> res) {
if (target == 0) {
res.add(new ArrayList<>(list));
return res;
}
for (int i = index; i < candidates.length;i++) {
// 剪大枝(优化)
if (target < 0) {
break;
}
// 剪小枝(去重)
if (i > index && candidates[i] == candidates[i-1]) {
continue;
}
list.add(candidates[i]);
dfs(candidates,target-candidates[i],i+1,list,res);
// 回溯
list.remove(list.size()-1);
}
return res;
}
示例
- 测试用例
int[] candidates = {1,2,2,2,5};
int target = 5
.
- 解释
递归:
target = 5
target = 5 - 1 = 4
target = 4 - 2 = 2
target = 2 - 2 = 0
==>res.add(new ArrayList<>(list)
==> 回溯:
==>list.remove(list.size()-1).
target = 2
i == 3 > index == 2 && candidates[3] == candidates[2] == 2
==>continue;
此时就达到了去重的目的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!