题目
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
我的想法
这道题与39. Combination Sum不同之处在于:
39题candidates不允许重复元素,但是结果中可以重复使用元素。
40题candidates允许重复元素,但结果中每个元素只能使用一次(1,1中两个1不同)
因此,为了能重复使用相同,元素39题迭代时,start = i
直接从当前循环数开始;而为了避免重复使用元素,40题迭代时start = i+1
需要从当前循环数的下一个数开始
因为这里的candidates允许重复元素,所以可能出现同一种组合出现多次的情况,比如:[1,1,1,2,3,3,5] target = 4
重复情况有两种:
- 首数字重复,即上述例子中,如果
target = 8
,[3,5]
的组合有2种。这种情况下,重复的组合不出现在同一个循环中。如果是首数字,其start
一定为0,因此只要是满足(start == 0 && i > 0) && candidates[i] == candidates[i-1]
则跳过 - 非首数字重复,即上述例子中,如果
target = 4
,[1,3]
的组合有6种。这种情况下,重复的组合出现在同一个循环中,因此满足(i > start) && candidates[i] == candidates[i-1]
则跳过
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ls = new ArrayList<>();
Arrays.sort(candidates);
backtrack(candidates, ls, new ArrayList<Integer>(), 0, 0, target);
return ls;
}
private void backtrack(int[] candidates, List<List<Integer>> ls, List<Integer> temp, int start, int count, int target) {
if(count == target) {
ls.add(new ArrayList<Integer>(temp));
return;
}
for(int i = start; i < candidates.length; i++) {
if(count + candidates[i] > target) continue;
//滤去重复项
if(((i > start) || (start == 0 && i > 0)) &&
candidates[i] == candidates[i-1]) continue;
temp.add(candidates[i]);
count += candidates[i];
backtrack(candidates, ls, temp, i+1, count, target);
count -= temp.remove(temp.size() - 1);
}
}
}
解答
leetcode solution: Backtrack
别人的写法更加简练。但是性能更差,原因待分析
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> list = new LinkedList<List<Integer>>();
Arrays.sort(candidates);
backtrack(list, new ArrayList<Integer>(), candidates, target, 0);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] cand, int remain, int start) {
if(remain < 0) return; /** no solution */
else if(remain == 0) list.add(new ArrayList<>(tempList));
else{
for (int i = start; i < cand.length; i++) {
if(i > start && cand[i] == cand[i-1]) continue; /** skip duplicates */
tempList.add(cand[i]);
//改变target的值来进行控制
backtrack(list, tempList, cand, remain - cand[i], i+1);
tempList.remove(tempList.size() - 1);
}
}
}