40.组合总和Ⅱ
40.组合总和Ⅱ
题目
给定一个数组 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]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
思路
candidates 中的每个数字在每个组合中只能使用一次。
这是与39题不同的地方,这个问题还比较好解决,使用startIndex,但是startIndex开始的区间不再是i了,而是i+1,因为只能用一次。
解集不能重复问题
39题:组合的数字可以重复,数组candidates是无重复元素的
通过startIndex来控制,但是本题中已经用其来控制数字在组合中只能使用一次了,之前的方法行不通了
举例:如[2,3,5] 2本身组合请,2与3组合情况,2与5组合情况,2,3,5组合情况,3本身组合情况,3,5组合情况,那么从5开始只用考虑5本身组合情况
本题:组合数字不能重复,数组candidates是有重复的元素的。
那么应该如何解决解集不能重复的问题?
1.在求出结果之后再去重
2.边搜索边去重
我选择的是边搜索边去重,在搜索的过程中检测分支是否会出现重复结果
candidates进行排序
但是按照之前做组合题的思路并不能知道怎么避免重复。去看了看别人的题解之后学习到了一种新的解题思路,对输入数组进行排序
进行排序后似乎有了规律!每一个分支可以出现重复的数,但是每一层并不能出现重复的数
那么为什么需要排序?重新做了39得出结论:排序的主要目的是剪枝★★★★★
现在理解了排序之后,就要思考如何去重,去重就是去掉重复的元素。
组合问题可以抽象为树形结构,那么重复需要考虑是指同一个树枝重复还是同一层重复。
从图中不难看出,是同一层重复,第一次出现的1已经考虑了和2组合的情况,第二次的1就不用再考虑和2组合的情况了。
题解
以下代码的前提是已经排好序了。
Arrays.sort(candidates);
Arrays.sort(int[] a)
这种形式是对一个数组的所有元素进行从小到大的排序,直接修改原数组。
递归函数的返回值以及参数
candidates:从candidates数组取值
target:组合的总数
startIndex:控制candidates数组的取值区间,
sum:路径总和
对于组合问题:组合无序!
- 多个集合取组合,各个集合之间互不影响,就不用startIndex
- 一个集合取组合,就需要startIndex
List<List<Integer>> res= new ArrayList<List<Integer>>(); //结果集
List<Integer> path = new ArrayList();//记录当前的路径
void backtracking(int[] candidates,int target,int startIndex,int sum);
递归的终止条件
if(sum==target){
res.add(new ArrayList(path));
return;
}
单层递归逻辑
前两步和39题没什么区别,主要的区别就在这个,因为取值不能重复,所以startIndex+1
剪枝
如果加上当前值已经比目标大了,由于是有序的,后面的元素加上sum一样比目标大
如何表示是同一层重复?同一层某个元素只能出现一次。
candidates[i]==candidates[i-1]
可以表示是重复的元素,这个是很容易想到的。
但是需要考虑越界问题,重复应该是从每一层的第二个元素开始重复。因为第一个元素前面没有与他相比的元素了。i>startindex
for(int i=startIndex;i<candidates.length;i++){
if(sum+candidates[i]>target)break;
if(i>startindex&&candidates[i]==candidates[i-1])continue;
path.add(candidates[i]);
backtracking(candidates,path,i+1,sum+candidates[i]);
}
path.remove(path.size()-1);
代码
class Solution {
List<List<Integer>> res;
List<Integer> path;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
res= new ArrayList<List<Integer>>();
path = new ArrayList<>();
Arrays.sort(candidates);
if(candidates.length==0)return res;
backtracking(candidates,target,0,0);
return res;
}
void backtracking(int[] candidates,int target,int startIndex,int sum){
if(sum==target){
res.add(new ArrayList(path));
return;
}
for(int i=startIndex;i<candidates.length;i++){
int cur = candidates[i];
if(sum+cur>target)break;
if(i>startIndex && cur==candidates[i-1])continue;
path.add(candidates[i]);
backtracking(candidates,target,i+1,sum+cur);
path.remove(path.size()-1);
}
}
}