题目:力扣https://leetcode-cn.com/problems/combination-sum-ii/
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Deque<Integer> path = new ArrayDeque<Integer>();
Arrays.sort(candidates);
backTrack(candidates,target,0,path,ans);
return ans;
}
private void backTrack(int[] candidates,int target,int begin,Deque<Integer> path,List<List<Integer>> ans){
//出口
if(target == 0){
ans.add(new ArrayList<>(path));
return;
}
for(int i=begin;i<candidates.length;i++){
// 大剪枝
if (target - candidates[i] < 0) {
break;
}
//小剪枝
if (i>begin && candidates[i]==candidates[i-1]) {
continue;
}
path.addLast(candidates[i]);
backTrack(candidates,target-candidates[i],i+1,path,ans);
path.removeLast();
}
}
}
思路:这题和leetcode39.组合总和 大同小异。不同点点:leetcode37中的candidates[]数组中的元素可以重复用,而这题中的candidates[]数组中的元素中能用一次。一开始我想直接删掉那些元素数量超额的答案,但是感觉这题就变查重题目而且时间复杂度会很大。因此,套用39题中的大致结构,学习题解然后改造原有的那份代码。大概思路仍然是回溯,修改的地方主要是增加了剪枝。
1.主方法有些许改动,因为采取了剪枝的方法(后面详述),所以不需要用HashSet去重,所以那部分的代码删去了。为了方便后续剪枝,再调用backTrack()之前,先把candidates[]数组排序。backTrack()仍然一个递归的方法,只是里面添加了些剪枝条件。
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Deque<Integer> path = new ArrayDeque<Integer>();
Arrays.sort(candidates);
backTrack(candidates,target,0,path,ans);
return ans;
}
2.写一个回溯的方法,基本就是leetcode39.组合总和复制粘贴。相比于leetcode39.组合总和candidates中元素可以多次使用,本题每个元素只能使用一次,因此,不得不多了一个begin变量记录遍历数组的起始位置。出口的判断条件仍然不变。增加了大小剪枝两部分代码:大兼职,即使当target-candidate[i]<0时,candidate[i]、candidate[i+1]、candidate[i+2]和candidate[i+n]等等都不要继续判断了,这些“枝”都会被剪去;小剪枝,当candidates[i]==candidates[i-1]时可以把这条“枝”剪去,这样就避免了重复解。但是这些操作都需要一个条件:candidates[]有序,所以前面需要再调用backTrack()之前将数组升序排序。与leetcode39.组合总和不一样的地方还有一个,39题递归调用backTrack()是传入同一个candidates[]数组,这题一个元素只能用一次,因此递归调用的时候需要传入一个begin表示遍历candidateas[]数组的起始位置,并且每一次递归都需要让begin前进一步,即i+1。
private void backTrack(int[] candidates,int target,int begin,Deque<Integer> path,List<List<Integer>> ans){
//出口
if(target == 0){
ans.add(new ArrayList<>(path));
return;
}
for(int i=begin;i<candidates.length;i++){
// 大剪枝
if (target - candidates[i] < 0) {
break;
}
//小剪枝
if (i>begin && candidates[i]==candidates[i-1]) {
continue;
}
path.addLast(candidates[i]);
backTrack(candidates,target-candidates[i],i+1,path,ans);
path.removeLast();
}
}