给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:
- 所有数字(包括目标数)都是正整数
- 解集不能包含重复的组合
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
这道题和 [回溯法] – 39 - 组合总和 - Python + Java原理上是一样的,只不过要求候选数组中的数字只能被使用一次,且解集中不能包含重复的组合。因此,首先不同之处在于下一次选择的位置是当前索引的下一个位置,即 i + 1。而且只在最后保存合法结果的时候判断一下,它在当前的解集中是否已经存在。如果已有,则直接进行下面的枚举过程;如果没有,则保存当前结果到解集,然后再进行下面的枚举。
Java解题代码:
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> results = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
Arrays.sort(candidates);
if (candidates.length == 0 || candidates[0] > target){
return results;
}
dfs(track, candidates, target, results, 0);
return results;
}
public void dfs(LinkedList<Integer> track, int[] candidates, int target, List<List<Integer>> results, int start) {
if(target == 0){
if (!results.contains(track)) {
results.add(new LinkedList<>(track));
return;
}
}
for (int i = start; i < candidates.length; i++) {
if (i == candidates.length || candidates[i] > target){
break;
}
track.add(candidates[i]);
dfs(track, candidates, target - candidates[i], results, i + 1);
track.removeLast();
}
}
}
Python解题代码
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
if candidates == [] or min(candidates) > target:
return []
l = len(candidates)
results = []
def backrtrack(candidates, target, track):
if target == 0 and track not in results:
results.append(track.copy())
return
for i in range(len(candidates)):
if i == l or candidates[i] > target:
break
track.append(candidates[i])
backrtrack(candidates[i + 1:], target - candidates[i], track)
track.pop()
candidates = sorted(candidates)
backrtrack(candidates, target, [])
return results
如果采用在最后进行结果判重的操作进行剪枝,那么时间复杂度相对较高。因为,只要得到了一个合法的结果就需要进行一次判断。更好的方式是在选择之前进行判断,执行剪枝操作。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> results = new LinkedList<>();
LinkedList<Integer> track = new LinkedList<>();
Arrays.sort(candidates);
if (candidates.length == 0 || candidates[0] > target){
return results;
}
dfs(track, candidates, target, results, 0);
return results;
}
public void dfs(LinkedList<Integer> track, int[] candidates, int target, List<List<Integer>> results, int start) {
if(target == 0){
results.add(new LinkedList<>(track));
return;
}
for (int i = start; i < candidates.length; i++) {
if (i > start && candidates[i] == candidates[i - 1]){
continue;
}
if (i == candidates.length || candidates[i] > target){
break;
}
// 做出选择
track.add(candidates[i]);
dfs(track, candidates, target - candidates[i], results, i + 1);
track.removeLast();
}
}
}
Python的实现代码同理
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
if candidates == [] or min(candidates) > target:
return []
l = len(candidates)
results = []
def backrtrack(candidates, target, track):
if target == 0:
results.append(track.copy())
return
for i in range(len(candidates)):
if i > 0 and candidates[i] == candidates[i -1]:
continue
if i == l or candidates[i] > target:
break
track.append(candidates[i])
backrtrack(candidates[i + 1:], target - candidates[i], track)
track.pop()
candidates = sorted(candidates)
backrtrack(candidates, target, [])
return results