题目
- 链接:
- 题目描述:
给定一个数组 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;
此时就达到了去重的目的。
浙公网安备 33010602011771号