LeetCode/40. 组合总和 II

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明:

所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:

1
2
3
4
5
6
7
8
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

示例 2:

1
2
3
4
5
6
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]

来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

本题和39. 组合总和类似,但是多出了限制条件,要求排列组合不能重复。我们先用最原始的思路在39. 组合总和上进行改造,为了避免重复,我们可以效仿47. 全排列 II的样子,创建一个used数组,用来标记当前元素是否已经被使用,接着,我们在每次backtrack的时候判断result里面是否有当前满足条件的track如果有的话就直接跳过,最后就是常规的回溯算法的标准框架。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
List<List<Integer>> result = new LinkedList<>();

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
LinkedList<Integer> track = new LinkedList<>();
//设置标记数组用来判断当前数字是否已经被使用
boolean[] used = new boolean[candidates.length];
//对candidates数组进行排序
Arrays.sort(candidates);
backtrack(candidates, target, 0, track, used);
return result;
}

void backtrack(int[] candidates, int target, int start, LinkedList<Integer> track, boolean[] used) {
//判断结束条件
if (target == 0) {
//判读result结果数组中是否包含当前满足条件的排列
if (result.contains(track))
return;
result.add(new LinkedList<>(track));
return;
}
if (target < 0) {
return;
}

for (int i = start; i < candidates.length; i++) {
if (used[i]) {
continue;
}
track.add(candidates[i]);
used[i] = true;
backtrack(candidates, target - candidates[i], i, track, used);
used[i] = false;
track.removeLast();
}
}
}

这个结果说实话,是没有办法让人满意的,我们来思考一下如何优化。如果排序后,candidates中相邻两个元素相等,其实排列的结果是重复的,我们可以直接跳过。而且,还有一种更好的办法替代used数组,就是让每次backtrack传进去的start + 1

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
List<List<Integer>> result = new LinkedList<>();

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
LinkedList<Integer> track = new LinkedList<>();
//对candidates数组进行排序
Arrays.sort(candidates);
backtrack(candidates, target, 0, track);
return result;
}

void backtrack(int[] candidates, int target, int start, LinkedList<Integer> track) {
if (target == 0) {
result.add(new LinkedList<>(track));
return;
}
if (target < 0) {
return;
}

for (int i = start; i < candidates.length; i++) {
//如果candidates[i] == candidates[i - 1]证明排列重复,我们可以直接跳过
if (i > start && candidates[i] == candidates[i - 1]) {
continue;
}
track.add(candidates[i]);
//这里注意start要传入i+1
backtrack(candidates, target - candidates[i], i + 1, track);

track.removeLast();
}
}
}

Comments