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] ]
这道题是39. Combination Sum的变体,同样是给出一个目标数target(T),在candidates数组(C)中找到和为T的所有可能的组合,主要区别就是candidates数组中的数字不能重复选择。
在39.Combination Sum的代码中,如果只是单纯地把递归调用的第二个参数right(或left)从i改成i - 1(或i + 1),就会出现重复的组合,例如,若C为(2, 2, 3, 3, 6, 7),则运行结果为[(2, 2, 3), (2, 2, 3), (7)],可见,(2, 2, 3)重复了一次,因为两个(2, 2, 3)中的3是不一样的,一个是C[2],另一个是C[3]。因此,需要做出一定的改动(第5行和第21行),在遍历的过程中,当发现该数字C[i]与前驱(backtrack2)或后继(backtrack1)是一样的,则直接跳过,不进行任何判断,因为以C[i]这个数字开头的遍历早已在其前驱(backtrack2)或后继(backtrack1)进行过一次。
从本质上讲backtrack1和backtrack2没有什么区别,只是遍历方向不一样,但有趣的是,在39. Combination Sum中用两种函数分别提交得到的Run time相差还是挺大的,分别是29ms和42ms,backtrack2用的时间长些。然而更有趣的是,在本题中,用两种函数得到的Run time分别是22ms和13ms,这次backtrack1用的时间更长些。
算法的时间复杂度仍然难以计算,但可以知道,在极端情况下——candidates数组中所有数字不重复且它们的和小于target,时间复杂度为O(n!)。
代码如下:
class Solution {
public:
void backtrack1(vector<int> com, int right, int target) {
for (int i = right; i >= 0; i--) {
if (i < right && candidates[i] == candidates[i + 1]) continue;
if (candidates[i] > target) continue;
else if (candidates[i] == target) {
vector<int> tmp = com;
tmp.push_back(candidates[i]);
res.push_back(tmp);
}
else {
vector<int> tmp = com;
tmp.push_back(candidates[i]);
backtrack1(tmp, i - 1, target - candidates[i]);
}
}
}
void backtrack2(vector<int> com, int left, int target) {
for (int i = left; i < candidates.size(); i++) {
if (i > left && candidates[i] == candidates[i - 1]) continue;
if (candidates[i] > target) break;
else if (candidates[i] == target) {
vector<int> tmp = com;
tmp.push_back(candidates[i]);
res.push_back(tmp);
}
else {
vector<int> tmp = com;
tmp.push_back(candidates[i]);
backtrack2(tmp, i + 1, target - candidates[i]);
}
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
this->candidates = candidates;
backtrack1(vector<int>(0), candidates.size() - 1, target);
//backtrack2(vector<int>(0), 0, target);
return res;
}
private:
vector<int> candidates;
vector<vector<int>> res;
};