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进行排序

但是按照之前做组合题的思路并不能知道怎么避免重复。去看了看别人的题解之后学习到了一种新的解题思路,对输入数组进行排序

image

进行排序后似乎有了规律!每一个分支可以出现重复的数,但是每一层并不能出现重复的数

那么为什么需要排序?重新做了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);
	}
    }

}
posted @ 2021-05-31 16:44  rananie  阅读(35)  评论(0编辑  收藏  举报