40. Combination Sum II

来源:互联网 发布:美国电影推荐 知乎 编辑:程序博客网 时间:2024/06/18 22:27

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C 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.

For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8
A solution set is: 

[  [1, 7],  [1, 2, 5],  [2, 6],  [1, 1, 6]]

recursion中一道非常经典的问题 与Combination Sum的区别是candidates有可能有重复元素

以下是三种常见的解法

public List<List<Integer>> combinationSum2(int[] candidates, int target) {    Arrays.sort(candidates);//1    List<List<Integer>> result = new ArrayList<>();    combine(candidates, target, 0, new ArrayList<>(), result);    return result;}private void combine(int[] candidates, int target, int i, List<Integer> cur, List<List<Integer>> result) {    if(target == 0) {        result.add(new ArrayList<>(cur));//2        return;    }    for (; i<candidates.length && candidates[i]<=target; i++) {        cur.add(candidates[i]);        combine(candidates, target-candidates[i], i+1, cur, result);        cur.remove(cur.size()-1);        while (i<candidates.length-1 && candidates[i+1]==candidates[i]) i++;//3    }}

重点在于标红的地方 首先对数组进行排序 是为了让相同的元素在一起 方便后面防重

第二处标红的地方 如果写的是

result.add(cur);

那最终的结果都是相同的元素 

[

[1,1,6],

[1,1,6],

[1,1,6],

[1,1,6]

]

因为cur是引用数据类型 在方法间传递的是引用 后面会被修改

在第三处标红的地方 就是防重

整个for循环的意思 是选择每次放入cur的最小元素

比如example中的输入,排序后是

1,1,2,5,6,7,10

第一次会首先放入1 第二次 因为1已经做过最小元素 所以不会再以1为最小元素

那会不会把[1,1,6]种种情况排除掉呢 答案是不会的 因为i=0时 会先放入1 target=8-1=7 

然后第二个1还是小于target 所以会被放入

所以如果for循环的地方你这样写

for (; i<candidates.length && candidates[i]<=target; i++) {    if (i>0 && candidates[i]==candidates[i-1]) continue;    cur.add(candidates[i]);    combine(candidates, target-candidates[i], i+1, cur, result);    cur.remove(cur.size()-1);}

就会把[1,1,6]给排除掉 因为第二个1加入不进来


为了方便解释 我们把2个1进行区分 

1,1',2,5,6,7,10

最终结果中会出现1,2,5 那么我们排除了那种情况呢 排除的是1',2,5

对于这个题目 还有两种常见写法

1.使用一个boolean[] visited 存储哪些元素已经被访问过

2.选取一个元素之后 把此元素从原集合中删除 这样后面就不会出现重复访问的问题了

但是这两种方式都需要额外空间 也会让代码看上去不简洁

原创粉丝点击