组合总和
题干
题目:给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
说明:
-
2 <= candidates[i] <= 40,数组中都是正数
-
解集不能包含重复的组合
思路和代码
同样是组合,但这道题不限制组合的大小,也不限制同一数字的使用次数。之前在做组合题目时,每用一个数字,下一个组合就不能用之前用过的,但在这道题里,每考虑一个新组合时本身这个数字能再次使用,这就是在原组合题目的基础上要修改的地方。
递归法
-
递归参数和返回值:参数是目标和 target、组合之和 sum、传入的集合 candidates、起始的元素位置 startIndex;无返回值。
-
递归结束的条件:当组合之和 sum 大于等于 目标和target时就要返回。
-
递归顺序:先确定组合中的一个元素,再递归寻找下一个元素凑组合。但和之前不同的是,我们递归寻找下一个元素时还可以是自身,因为可以重复,因为递归时候传入的起始位置仍为 i,即 backTracking(target, sum, candidates, i),而不是 i+1。
class Solution {
public:
vector<int> composition; // 记录组合
vector<vector<int>> result; // 记录所有组合的结果
void backTracking(int target, int &sum, vector<int>& candidates, int startIndex){
if (sum == target){ // sum 记录组合之和
result.push_back(composition);
return;
} else if (sum > target){
return;
}
for (int i = startIndex; i < candidates.size(); ++i) {
int num = candidates[i];
composition.push_back(num);
sum += num;
backTracking(target,sum,candidates,i); // 这里输入应该是 i,而不是 i+1,因为元素可以重复
composition.pop_back();
sum -= num;
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
int sum = 0;
backTracking(target,sum,candidates,0);
return result;
}
};
递归优化:剪枝
优化:对数组排序并减少不必要的 for 循环
数组 candidates 本身是无序的,因此我们在遍历到 sum > target 时返回后,又需要继续遍历下一个元素。但如果数组是升序排列,当 sum > target 时,也不需要往下再遍历了,因为之后的数组元素只会更大,不可能再出现 sum = target 的情况,可以直接终止循环。
class Solution {
public:
vector<int> composition;
vector<vector<int>> result;
void backTracking(int target, int &sum, vector<int>& candidates, int startIndex){
if (sum == target){
result.push_back(composition);
return;
}
for (int i = startIndex; i < candidates.size() && sum+candidates[i] <= target; ++i) {
// 剪枝操作,如果加入当前元素已经 > target 直接终止循环
int num = candidates[i];
composition.push_back(num);
sum += num;
backTracking(target,sum,candidates,i);
composition.pop_back();
sum -= num;
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
int sum = 0;
sort(candidates.begin(),candidates.end()); // 升序排列
backTracking(target,sum,candidates,0);
return result;
}
};
组合总和Ⅱ
题干
题目:给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
说明:
-
1 <= candidates[i] <= 50,数组中只有正数
-
数组中可能有重复元素
思路和代码
本题和上一题不一样的地方就在于数组 candidates 中可能包含重复的数字,而且每个数字在一次组合中只能使用一次,不可以多次使用。由于有重复的数字,这样递归求得的组合中可能会重复,因此我们需要先对 candidates 去重,重复出现的数字其实表明了该数字在组合中可以用多少次,我们需要记录下不同数字的使用次数。
递归法
class Solution {
public:
int count[51]; // 记录不同数字的使用次数, 1 <= candidates[i] <= 50
vector<int> composition; // 记录组合
vector<vector<int>> result; // 记录所有组合的结果
void backTracking(vector<int>& candidates, int target, int sum, int startIndex){
if (sum == target){
result.push_back(composition);
return;
} else if (sum > target){
return;
}
for (int i = startIndex; i < candidates.size(); ++i) {
int num = candidates[i];
sum += num;
composition.push_back(num);
count[num]--; // 此时数字 num 已被使用一次,故次数减一
if (count[num] > 0) backTracking(candidates,target,sum,i); // 若还有使用次数,则重复使用数字
else backTracking(candidates,target,sum,i+1); // 若没有使用次数,则使用下一个数字
composition.pop_back();
sum -= num;
count[num]++;
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> newCandidates; // 记录去重后的新数组
for (int i : candidates) {
if (count[i] == 0){
newCandidates.push_back(i);
}
count[i]++; // 记录下每个数字的使用次数
}
backTracking(newCandidates,target,0,0);
return result;
}
};
递归优化:剪枝
同样是对 candidates 数组进行升序排序,这样当 sum > target 时无需再进入循环。
class Solution {
public:
int count[51]; // 1 <= candidates[i] <= 50
vector<int> composition;
vector<vector<int>> result;
void backTracking(vector<int>& candidates, int target, int sum, int startIndex){
if (sum == target){
result.push_back(composition);
return;
}
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; ++i) {
// 剪枝,减少循环
int num = candidates[i];
sum += num;
composition.push_back(num);
count[num]--;
if (count[num] > 0) backTracking(candidates,target,sum,i);
else backTracking(candidates,target,sum,i+1);
composition.pop_back();
sum -= num;
count[num]++;
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> newCandidates; // 记录去重后的新数组
for (int i : candidates) {
if (count[i] == 0){
newCandidates.push_back(i);
}
count[i]++; // 记录下每个数字的使用次数
}
sort(newCandidates.begin(),newCandidates.end()); // 排序
backTracking(newCandidates,target,0,0);
return result;
}
};
标签:num,target,22,int,08,随想录,vector,candidates,sum
From: https://blog.csdn.net/chan_lay/article/details/141438877