1.candidates
中的 同一个 数字可以 无限制重复被选取 。
2.所有 不同组合
要求不同组合的话就需要设置参数startlex确保不会出现重复组合,即代码
for(int i=startlex;i<candidates.size();i++)
以
candidates =[2,3,6,7],
target =7
为例,如果将循环条件设置为
for(int i=0;i<candidates.size();i++)
那么会出现[2,2,3]和[3,2,2]这两种重复的情况。
第二个就是同一个数字可以无限选取,在设置递归条件时设置为
breaking(candidates,target,i);
这样即使这次使用了candidates[i],进入下一层递归时还可以使用candidates[i]。
主要还是以上两个条件不能弄混了。
——————————————————————————————————————————-
还有一个终止条件的处理,需要考虑到sum>target的情况
否则当sum>target时不会触发return,将会陷入死循环。
class Solution {
public:
vector<vector<int>>result;
vector<int>s1;
int sum=0;
void breaking(vector<int>& candidates, int target,int startlex)
{
if(sum==target)
{
result.push_back(s1);
return;
}
if(sum>target)
return ;
for(int i=startlex;i<candidates.size();i++)
{
s1.push_back(candidates[i]);
sum+=candidates[i];
breaking(candidates,target,i);
sum-=candidates[i];
s1.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
breaking(candidates,target,0);
return result;
}
};
40.组合总和I
这题主要是去重的思想,借用代码随想录的思路
同一个树层不能出现重复元素,同一个树枝可以出现重复元素。
树层即为path中相同位置的不同元素导致的path的不同情况(多种path)
树枝即为组成一种path所依次插入元素的过程(单种path)
树层不重复是为了防止相同组合的出现
例子:
我们用1(1)代指第一个1,1(2)代指第二个1
树层不重复是为了防止[1(1),2,5],[1(2),2,5]的情况出现。
树枝不重复是为了防止同一种组合中出现相同的元素
还是以上图为例
防止[1(1),1(2),6]的情况出现。
现在问题是如何实现树层不重复,就是在1(1)进入path时,需要留下记号,防止1(2)进入path的相同位置。
如代码随想录的方法:
我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:
- used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
- used[i - 1] == false,说明同一树层candidates[i - 1]使用过
以[1(1),1(2),2]为例,当我们在进行树枝的构造时,使用过的元素,used对应的位置一定全是true,
for循环中backtracking下面的代码都没有用到,可以这样说
树枝的构造过程就是不断将used数组对应位置由false变为true的过程
树层的构造,就是这个循环已经完成了一次(i=0),进入下一层循环了(i=1)
此时1(1)为首元素的树层情况已经构建完成,1(1)对应的位置used已经又由true变为false了,所以考虑1(2)时,1(2)就不会再进入path了。
那么为什么used初始需要设置成false?是因为相同元素优先倾向同一树层已经使用过!
首先我们确定,只有前后两个元素相同才会触发used导致的跳跃事件
以[2,2,2],target=2为例,如果哦我们将used设置为true,现在的used就是[1,1,1]。
当i=0循环结束时。此时used已经变成[0,1,1],进入i=1的循环,由于满足continue的条件,i=1被跳过了,此时used仍为[0,1,1]
当i=2时,因为used[1]==1,不满足跳过条件,所以i=2循环会正常进行!!
本质原因就是连续出现continue情况时,used数组会因为跳过无法正确记录元素是否在同一树层使用过!(i=1)
所以我们将used初始值设置为false,相同元素优先倾向同一树层已经使用过!
131.分割回文串
此题是分割问题,进入循环的参数不再是下标,而是字符串分割点。
终止条件就是分割点大于等于size就会结束
至于为什么不是size-1时结束
首先就是size-1时还是可以继续分割的,(字符串最后一个字母单独分割出来),当这一分割结束时,
backtracking(s, i + 1);
进入下一层递归,此时分割点变为size,分割才算正式结束
还有就是循环中的代码执行
我们需要这样的效果:
是回文,正常递归和回溯
不是回文,直接跳过本次循环。
所以需要用if提前判断是否是回文,不是回文直接跳过。
可能有人有问题,你path.push_back(str);是在if{}内的,而 path.pop_back();不在if{}内,也就是说
插入数据是有条件的,排除数据是无条件的,万一还没进入数据就需要排出数据怎么办,所以
path.pop_back();也应该写在if内部
仔细分析代码可以明白,当跳过本次循环时,后面的pop是不会执行的!所以不会存在只出不入的情况。
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) { // 是回文子串
// 获取[startIndex,i]在s中的子串
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
} else { // 不是回文,跳过
continue;
}
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经添加的子串
}
}