前言
LeetCode题目:LeetCode 39、40、131
Takeaway:回溯算法的思路和模板的应用,这次还有回溯算法在分割类型题目中的应用;以及start(startindex)参数在回溯中的意义是什么,它能用来做什么,什么时候用(集合之间存在影响的时候),什么时候不用(集合可以重复);还有去重时,什么时候时枝去重,什么时候是层去重。
一、39
主要需要注意的就是startindex,注释说的很明确了。
class Solution {
public:
vector<vector<int>> ans;
vector<int> each;
// startindex 的作用是防出现同组重复
void traverse (vector<int>& candidates, int target, int sum, int startIndex){
if(sum > target){
return;
}
if(sum == target){
ans.push_back(each);
return;
}
for(int i=startIndex; i<candidates.size(); i++){
sum += candidates[i];
each.push_back(candidates[i]);
//这里是精髓! 如果传的是i+1,那么无法存在相同元素
traverse(candidates, target, sum, i);
sum -= candidates[i];
each.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
traverse(candidates, target, 0, 0);
return ans;
}
};
二、40
在上一题的基础上多了去重,去重分为枝叶去重和层去重,本题要求是层去重,那么利用used数组,当元素与前一个相同且used显示为false时,说明时同层相同,需要跳过去重(如果为ture,说明相同元素是上一层用的,不是同层,当前为其叶子,不需要进行枝叶去重)。
class Solution {
public:
vector<vector<int>> ans;
vector<int> each;
// startindex 的作用是防出现同组重复
void traverse (vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
if(sum > target){
return;
}
if(sum == target){
ans.push_back(each);
return;
}
// used 用来去重,为true表示是上一层用的,那么就可以继续用,为false表示上一层没用,那么说明这一层已经用过了,要去重跳过当前值
for(int i=startIndex; i<candidates.size(); i++){
if(i>=1 && candidates[i]==candidates[i-1] && used[i-1]==false){
continue;
}
sum += candidates[i];
each.push_back(candidates[i]);
used[i] = true;
//这里是精髓! 如果传的是i+1,那么无法存在相同元素
traverse(candidates, target, sum, i+1, used);
sum -= candidates[i];
each.pop_back();
used[i] = false;
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<bool> used(candidates.size(), false);
sort(candidates.begin(), candidates.end());
traverse(candidates, target, 0, 0, used);
return ans;
}
};
三、131
回溯算法解决分割类型题目,判断是否回文用双指针做,子串要左闭右闭。
class Solution {
public:
vector<vector<string>> ans;
vector<string> each;
//双指针判断回文
bool isPalindrome(const 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 traverse(string s, int start) {
if(start >= s.size()) {
ans.push_back(each);
return;
}
for(int i=start; i<s.size(); i++) {
//start到i是否是回文串,这里一开始start==i,这是因为a自己就是回文串
if(isPalindrome(s, start, i)){
string str = s.substr(start, i - start + 1);
each.push_back(str);
}else{
//不是就下一个,比如 ab不是,但是继续往下走如果是aba那就又是回文了
continue;
}
//然后从i+1继续往下找新的回文,因为start到i是回文子串了
traverse(s, i+1);
//回溯
each.pop_back();
}
}
vector<vector<string>> partition(string s) {
traverse(s, 0);
return ans;
}
};
总结
回溯算法的思路和模板的应用,这次还有回溯算法在分割类型题目中的应用;以及start(startindex)参数在回溯中的意义是什么,它能用来做什么,什么时候用(集合之间存在影响的时候),什么时候不用(集合可以重复);还有去重时,什么时候时枝去重,什么时候是层去重。