给定一个数组 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第90的解法,代码如下:
具体可参考leetcode第90题的注释
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> result;
vector<int> item;
set<vector<int>> res_set; //去重使用的集合
sort(candidates.begin(),candidates.end()); //对nums数组进行排序
result.push_back(item);
generate(0, candidates, result, item, res_set);
vector<vector<int>> target_result; //储存最终结果
for(int i = 0; i < result.size(); i++){
int sum = 0;
for(int j = 0; j < result[i].size(); j++){
sum += result[i][j]; //计算个子集的和
}
if(sum == target){
target_result.push_back(result[i]);
}
}
return target_result;
}
private:
void generate(int i, vector<int>& candidates, vector<vector<int>> &result, vector<int>& item, set<vector<int>>& res_set){
if(i >= candidates.size()){
return;
}
item.push_back(candidates[i]);
if(res_set.find(item) == res_set.end()){ //如果res_set集合中,无法找到item
result.push_back(item); //将item放入result数组中
res_set.insert(item); //将item放入去重集合res_set中
}
generate(i+1, candidates, result, item, res_set);
item.pop_back();
generate(i+1, candidates, result, item, res_set);
}
};
但该解法在运行的时候,出现了错误原因是时间超出了限制
最后执行的输入:[14,6,25,9,30,20,33,34,28,30,16,12,31,9,9,12,34,16,25,32,8,7,30,12,33,20,21,29,24,17,27,34,11,17,30,6,32,21,27,17,16,8,24,12,12,28,11,33,10,32,22,13,34,18,12]
27
无论是回溯法或者位运算,整体时间复杂度O(2^n)
该操作应该利用到target = 8的条件,在搜索回溯过程中进行剪枝操作:
递归调用时,计算已经选择的和sum,若sum > target,不再进行更深的搜索,直接返回
例如:
nums[] = [10,1,2,7,6,1,5]
target = 8;
代码如下,可以对比上述代码,理解剪枝操作
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> result;
vector<int> item;
set<vector<int>> res_set; //去重使用的集合
sort(candidates.begin(),candidates.end()); //对nums数组进行排序
//result.push_back(item);
generate(0, candidates, result, item, res_set, 0, target);
//vector<vector<int>> target_result; //储存最终结果
//for(int i = 0; i < result.size(); i++){
// int sum = 0;
// for(int j = 0; j < result[i].size(); j++){
// sum += result[i][j]; //计算个子集的和
// }
// if(sum == target){
// target_result.push_back(result[i]);
// }
//}
return result;
}
private:
void generate(int i, vector<int>& candidates, vector<vector<int>> &result, vector<int>& item, set<vector<int>>& res_set, int sum, int target){
if(i >= candidates.size() || sum > target){
return;
}
sum += candidates[i]; //sum为当前子集item中的元素和
item.push_back(candidates[i]);
if(target == sum && res_set.find(item) == res_set.end()){ //如果res_set集合中,无法找到item
result.push_back(item); //将item放入result数组中
res_set.insert(item); //将item放入去重集合res_set中
}
generate(i+1, candidates, result, item, res_set, sum, target);
sum -= candidates[i]; //回溯后,sum将nums[i]减去并从item中删去
item.pop_back();
generate(i+1, candidates, result, item, res_set, sum, target);
}
};
测试函数如下:
int main()
{
vector<int> nums;
nums.push_back(10);
nums.push_back(1);
nums.push_back(2);
nums.push_back(7);
nums.push_back(6);
nums.push_back(1);
nums.push_back(5);
vector<vector<int>> result; //最终数组
Solution solve;
result = solve.combinationSum2(nums, 8);
for (int i = 0; i < result.size(); i++){
if (result[i].size() == 0) {
printf("[]");
}
for (int j = 0; j < result[i].size(); j++) {
printf("[%d]", result[i][j]);
}
printf("\n");
}
return 0;
}
结果如下: