题目
给你一个 无重复元素 的整数数组 candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
示例
示例 1:
输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。仅有这两种组合。示例 2:
输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]示例 3:
输入: candidates = [2], target = 1 输出: []
分析
为了解决组合总和问题,我们可以使用回溯算法来生成所有可能的组合,并通过剪枝优化来提高效率。
回溯法
方法思路
回溯算法:通过递归遍历候选数组,每次选择一个数字并递归处理剩余的目标值,直到目标值为 0 或当前数字超过目标值。
剪枝优化:首先对候选数组进行排序,以便在遍历过程中遇到超过剩余目标值的数字时提前终止,减少无效递归。
允许重复选择:由于每个数字可以无限制重复使用,递归时从当前索引开始而非下一个索引,确保可以重复选择当前数字。
时间复杂度:最坏下 O(),
是数组的长度,
为组合的平均长度,但通过剪枝优化,实际效率较高
空间复杂度:O()
class Solution {
private:
vector<vector<int>> result;
vector<int> currentCombination;
void backtrack(vector<int>& candidates, int start, int remaining) {
if (remaining == 0) {
result.push_back(currentCombination);
return;
}
for (int i = start; i < candidates.size(); ++i) {
int num = candidates[i];
if (num > remaining) {
break; // 剪枝:后续数字更大,无需继续
}
currentCombination.push_back(num);
backtrack(candidates, i, remaining - num); // 允许重复选择当前数字,所以i不变
currentCombination.pop_back();
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end()); // 排序以便剪枝
backtrack(candidates, 0, target);
return result;
}
};