题目
给定一个数组 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]
]
思路
类似leetcode39 组合总和
区别在于39中没有重复数字,每个数字可以无限取(所以递归时的索引不一样);
而本题中有重复数字,每个数字只能取一次,关键点在于如何避免重复。所以可以先对数组排序,在for循环范围中如果当前数和前一个数相等,则跳过。
具体如:candidates 1 1 2 5 6 7 10
分支1: i = 0 —func— index=1(递归)
A:candidates[0]被选取,candidates[1]不被选取
B:candidates[0]被选取,candidates[1]被选取
分支2: i =0—— i =1(for循环)
C:candidates[0]不被选取,candidates[1]不被选取
D:candidates[0]不被选取,candidates[1]被选取
D与A重复
实现
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> res;
vector<int> tmp;
sort(candidates.begin(), candidates.end());
func(candidates, target, 0, &res, &tmp);
return res;
}
void func(const vector<int>& candidates, int target, int index, vector<vector<int>>* res,
vector<int>* tmp) {
if (target == 0) {
res->push_back(*tmp);
} else if (target > 0) {
for (int i = index; i < candidates.size(); ++i) {
// i > index, 要避免与循环的i-1那个分支重复(选取与不被选取),这个时候不跳过就会重复
// 具体如:candidates 1 1 2 5 6 7 10
//当 index = 0, i = 1,candidates [1] == candidates [0]。
//for循环中,如果不跳过,这个分支的"i=0不被选取,i = 1被选取"将与
//"前一个分支的i = 0被选取, i=1(递归逻辑)不被选取"重复;
//这个分支的"i=1不被选取"将与"本分支的i=0不被选"重复
if (i > index && candidates[i] == candidates[i - 1]) {
continue;
}
if (target >= candidates[i]) {
tmp->push_back(candidates[i]);
func(candidates, target - candidates[i], i + 1, res, tmp);
tmp->pop_back();
}
}
}
}
};