文章目录
40. 组合总和 II
题目描述
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
示例 2:
提示:
- 1 <= candidates.length <= 100
- 1 <= candidates[i] <= 50
- 1 <= target <= 30
回溯算法
// 定义一个Solution类,用于解决组合总和II问题
class Solution {
public:
// 定义一个公共方法combinationSum2,它接受一个整数数组candidates和一个整数target,
// 返回一个二维整数数组,里面包含了所有可以使数字和为target的组合。
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
// 首先对数组进行排序,这会帮助我们更容易地找到组合,并且能够跳过重复的组合
sort(candidates.begin(),candidates.end());
// 输出排序后的数组元素,这一步通常是为了调试
for(int i=0;i<candidates.size();i++)
cout<<candidates[i]<<" ";
// 定义一个布尔向量used,用于标记candidates中的元素是否被用在当前的组合中
vector<bool> used(candidates.size(),false);
// 调用backstracking函数,开始回溯算法
backstracking(candidates, target, 0, 0, used);
// 返回所有找到的组合
return result;
}
private:
// 定义一个私有变量result,用于存储所有找到的组合
vector<vector<int>> result;
// 定义一个私有变量path,用于存储当前正在考虑的组合
vector<int> path;
// 定义一个私有函数backstracking,它实现了回溯算法
void backstracking(vector<int>& candidates,int target,int sum,int start,vector<bool>& used)
{
// 如果当前组合的数字和大于目标数target,则当前路径不可能为解,回溯
if(sum>target)
return;
// 如果当前组合的数字和等于目标数target,则找到了一个解,将它添加到result中
if(sum==target)
{
result.push_back(path);
return;
}
// 从start开始遍历candidates数组,尝试每个可能的元素
for(int i=start;i<candidates.size();i++)
{
// 跳过重复的数字,以避免重复的组合,这是因为数组已经排序
if(i>0 && candidates[i]==candidates[i-1] && !used[i-1])
continue;
// 将candidates[i]添加到当前路径
path.push_back(candidates[i]);
// 标记该元素为已使用
used[i]=true;
// 将candidates[i]的值加到当前和上,并递归调用backstracking,注意新的start是i+1,因为每个数字只能使用一次
sum+=candidates[i];
backstracking(candidates, target, sum, i+1, used);
// 回溯,将最后一个元素从路径中删除并从当前和中减去,取消标记该元素
path.pop_back();
used[i]=false;
// 从当前和中减去candidates[i]的值,为下一次迭代准备
sum-=candidates[i];
}
}
};
代码主要由以下几个部分组成:
- combinationSum2 公共方法:对给定数组排序,初始化用于记录元素使用情况的布尔向量,开始回溯搜索,最后返回结果。
- backstracking 私有方法:实现了回溯算法的核心逻辑,通过递归尝试每个可能的元素,直到找到所有的组合或者终止搜索。
- result 和 path 私有变量:分别用于存储找到的所有组合结果和当前递归路径中的组合。
- used 布尔向量:用于标记数组 candidates 中的元素是否已经被使用过,以防止在同一路径中重复使用。