目录
39. 组合总和:
问题描述:
给你一个 无重复元素 的整数数组 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 输出: []
实现代码与解析:
回溯:
class Solution {
public:
vector<int> path;//记录路径
int pathSum=0;//路径和
vector<vector<int>> result;//记录结果
void backtrack(vector<int> candidates,int startIndex,int target)
{
//若大于,则直接返回,寻找别的符合条件的
if(pathSum>target)
{
return;
}
if(pathSum==target)
{
result.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size();i++)
{
path.push_back(candidates[i]);//跟新路径
pathSum+=candidates[i];//跟新路径和
backtrack(candidates,i,target);//这里startIndex没有加一
path.pop_back();//回溯
pathSum-=candidates[i];//回溯
}
return;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
backtrack(candidates,0,target);
return result;
}
};
原理思路:
Leetcode:77. 组合、216. 组合总和 III(C++)_Cosmoshhhyyy的博客-CSDN博客
还是比较简单的,这里不再详细解释了,可以看看我前面写的几篇文章,这里主要讲讲需要注意的地方。
首先就是此题给的数组元素是可以重复使用的,所以下面这行递归代码中的 i 没有进行加一的操作作。
backtrack(candidates,i,target);//这里startIndex没有加一
其次就是这题也是可以进行剪枝操作的,不过首先需要将数组升序排序,我们在已知pathSum大于target的情况下,就没必要再进行该层后面的遍历且进入下一层递归了。
for(int i=startIndex;i<candidates.size()&&pathSum+candidates[i]<=target;i++)
下面给出剪枝版的代码:
剪枝版:
class Solution {
public:
vector<int> path;//记录路径
int pathSum=0;//路径和
vector<vector<int>> result;//记录结果
void backtrack(vector<int> candidates,int startIndex,int target)
{
if(pathSum==target)
{
result.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size()&&pathSum+candidates[i]<=target
;i++)
{
path.push_back(candidates[i]);//跟新路径
pathSum+=candidates[i];//跟新路径和
backtrack(candidates,i,target);//这里startIndex没有加一
path.pop_back();//回溯
pathSum-=candidates[i];//回溯
}
return;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target)
{
sort(candidates.begin(),candidates.end());
backtrack(candidates,0,target);
return result;
}
};
40. 组合总和 II:
问题描述:
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8, 输出: [[1,1,6],[1,2,5],[1,7],[2,6]]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 输出:[[1,2,2],[5]]
实现代码与解析:
回溯:
class Solution {
public:
vector<vector<int>> result;//记录结果
vector<int> path;//记录路径
int pathSum;//记录路径和
void backtrack(vector<int> candidates,int target,int startIndex)
{
if(pathSum>target) return;
if(pathSum==target)
{
result.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size();i++)
{
//去重
if(i>startIndex&&candidates[i]==candidates[i-1])
{
continue;//跳过本次循环
}
path.push_back(candidates[i]);
pathSum+=candidates[i];
backtrack(candidates,target,i+1);//递归
path.pop_back();//回溯
pathSum-=candidates[i];//回溯
}
return;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
sort(candidates.begin(),candidates.end());//升序排序
backtrack(candidates,target,0);
return result;
}
};
原理思路:
与上一题原理相似,先说说此题与上一题的区别,大家可以重点关注。
1、此题数组有重复元素,但是一个元素只能用一次。
2、一个结果之内可以有重复元素,因为给的数组里就有重复元素,但是结果与结果之间不能重复,所以要经行去重处理。
一般反应可能就是先排序,然后用上一题的写法先把结果找出来,然后放入set中去重,但是这样很容易超时,所以我们要在查找时就去重。
相对于上一题,大家可以看出其实就多了几行代码而已,如下:
//去重
if(i>startIndex&&candidates[i]==candidates[i-1])
{
continue;//跳过本次循环
}
去重之前还是要先给数组排序的,然后讲讲去重逻辑,其实很简单,如果写过
Leetcode:15. 三数之和(C++)_Cosmoshhhyyy的博客-CSDN博客
Leetcode:18. 四数之和(C++)_Cosmoshhhyyy的博客-CSDN博客
的话,应该可以很快就理解此代码的含义。
其实就是如果当前遍历的数和上一个遍历的数相同的话,就不再遍历此数,因为此值所找出来的结果已经被前一个数找齐了,这也就是为什么要排序的原因,就是为了让相同值的元素靠在一起。最主要的就是我让大家注意的第二条,要是看成一颗树的话,这里去的是同一层的元素,而不是同一树枝上的元素。
举个简单的例子吧,如图:
同样给出剪枝的代码,和上一题剪枝操作相同,如下:
剪枝版:
class Solution {
public:
vector<vector<int>> result;//记录结果
vector<int> path;//记录路径
int pathSum;//记录路径和
void backtrack(vector<int> candidates,int target,int startIndex)
{
if(pathSum==target)
{
result.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size()&&pathSum+candidates[i]<=target;i++)
{
//去重
if(i>startIndex&&candidates[i]==candidates[i-1])
{
continue;//跳过本次循环
}
path.push_back(candidates[i]);
pathSum+=candidates[i];
backtrack(candidates,target,i+1);//递归
path.pop_back();//回溯
pathSum-=candidates[i];//回溯
}
return;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
sort(candidates.begin(),candidates.end());//升序排序
backtrack(candidates,target,0);
return result;
}
};