40.组合II
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
思路:组合题目二,这个题目与组合一的区别在于,本题目中的candidates的每个元素只能使用一次,而且本题中的candidate数组中会有重复的元素。要对此进行去重
首先定义三个成员变量,一维和二维的数组和sum,sum代表累加的和。在回溯函数中如果当前的sum和等于目标值,将结果放到二维数组中并返回。然后继续往下看,通过for循环遍历目标数组(此时在for的循环条件里加入了剪枝的操作,当 当前的和加上当前对应的数组值小于等于目标值,才可以继续进入循环,否则的话就不进入循环,因为不满足条件进入循环也没必要了),如果i大于0(不能是第一层),并且当前对应的数组的值不能等于前一个值,如果相等,在遍历这第二个相同的值就没有意义了,因为前一个值所遍历的范围已经包含第二个值了,所以应该将第二个去重掉,(在这道题目中我定义了一个存储布尔类型的动态数组,并且它的大小与candidate的大小相同,来记录已经遍历过的元素,如果元素已经使用过,就设置为TRUE),在if条件中判断如果当前值与前一个相等并且前一个的used数组显示为FALSE。为什么一定是等于FALSE呢,等于TRUE不可以吗?如果等于TRUE的话,我们可能会遗漏一些正确的组合,如下图所示:
而我们实际要剪枝的是:也就是Carl哥说的树层和树枝去重(下图是树层,上图是树枝)
去重的条件大概就是这些,然后呢就是正常的回溯递归,注意还有一点的是我们依然采用了startIndex来更新递归后的搜索初始节点进行去重。
cpp
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
int sum;
void travelBacking(vector<int>& candidates, int target,int startIndex,vector<bool>& used){
if(sum==target){
res.push_back(path);
return ;
}
for(int i=startIndex;i<candidates.size()&&sum+candidates[i]<=target;i++){
if(i>0&&used[i-1]==false&&candidates[i]==candidates[i-1]){
continue;
}
sum+=candidates[i];
path.push_back(candidates[i]);
used[i]=true;
travelBacking(candidates,target,i+1,used);//每个数字在每个组合中只能使用一次
used[i]=false;
sum-=candidates[i];
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
int startIndex=0;
vector<bool> used(candidates.size(),false);
sort(candidates.begin(),candidates.end());
travelBacking(candidates,target,startIndex,used);
return res;
}
};
131.分割回文串
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是
回文串。
返回 s 所有可能的分割方案。
思路:分割字符串第一眼看似很难,但实际上与前面的组合题目很像的,需要增加的操作就是要定义一个判断回文串的函数,然后正常定义回溯函数,如果传入的startIndex要大于等于字符串长度,startIndex代表切割线,如果切割线等于字符串长度,代表切割到尾部了,将path存入result并返回,这是回溯三部曲的第二步,确认回溯终止条件,第一步是传入参数,第三步是确认单层递归逻辑,首先判断当前子串是否为回文串,如果是回文串进入循环,将其截取出来并存入字符串str中,注意截取时传入的参数是startIndex--字符串的起始位置和长度--i-startIndex+1,因为长度就是下标加1,如果非回文串的话,continue(continue
语句用于跳过当前循环的剩余部分,并立即开始下一次循环迭代);注意,有人可能不理解为什么开始值和结束值是startIndex和i呢? startIndex是起始位置,而i则是结束位置。
cpp
class Solution {
public:
vector<string> path;
vector<vector<string>> result;
bool isPartition(string s,int start,int end){//判断是否为回文串
for(int i=start,j=end;i<j;i++,j--){
if(s[i]!=s[j]){
return false;
}
}
return true;
}
void travelBacking(string s,int startIndex){
if(startIndex>=s.size()){
result.push_back(path);
return;
}
for(int i=startIndex;i<s.size();i++){
if(isPartition(s,startIndex,i)){
string str=s.substr(startIndex,i-startIndex+1);
path.push_back(str);
}else{
continue;
}
travelBacking(s,i+1);
path.pop_back();
}
}
vector<vector<string>> partition(string s) {
travelBacking(s, 0);
return result;
}
};
78.子集
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路:此题相对于上面的题目来说算是简单的了,同样的回溯模版,差别在于在它是取所有的集合,要在回溯函数开始时将path加入到二维数组中,然后正常将该题目抽象成树形结构遍历。如图所示:(选自代码随想录图解)
cpp
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
void travelBacking(vector<int>& nums,int startIndex){
res.push_back(path);
if(startIndex>=nums.size()){
return;
}
for(int i=startIndex;i<nums.size();i++){
path.push_back(nums[i]);
travelBacking(nums,i+1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
travelBacking(nums,0);
return res;
}
};
90.子集II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路: 和上一道题很像,主要就是和组合二大体思路的去重,树层去重和树枝去重,通过一个used数组来记录是否使用过这个数字,当当前的值与前一个值相等时,并且前一个值为FALSE,代表是树层去重,无需遍历当前的树枝了,因为前面的树枝遍历已经包含了这个的树枝遍历,还需要注意的一点是,因为子集是找出所以的组合并记录,所以要在回溯函数开头将一维数组存到二维数组中。
cpp
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
void travelBacking(vector<int>& nums,int startIndex,vector<bool>& used){
res.push_back(path);
if(startIndex>=nums.size()){
return;
}
for(int i=startIndex;i<nums.size();i++){
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false){
continue;
}
path.push_back(nums[i]);
used[i]=true;
travelBacking(nums,i+1,used);
used[i]=false;
path.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<bool> used(nums.size(),false);
sort(nums.begin(),nums.end());
travelBacking(nums,0,used);
return res;
}
};