总结
多数情况递归回溯的返回值是void;
递归存路径的时候,回溯pop了,就不用在存答案之后clear了,因为每层都会pop;
组合dfs的时候:
dfs进去就是利用for循环往下延申树枝之间的纵向交流,回溯回来就是利用for循环一层一层之间的横向交流;
涉及到去重,一般就是先排个序;
剪枝就是在for循环的时候剪了直接,不符合的情况就不要dfs了,一般配合排序;
use作用有两个:
树层去重(如1215,取了12就不能取21了,这种情况先排序为1125,前一个1已经把组合都给组了,后一个1就不能组了,这是重复);树枝去重(如1125,不能取112,树枝元素不能相同);
之所以要加个used,是因为单纯的candidates[i]==candidates[i-1]会把树层的 树枝的都给去重,题目只要求了树层or树枝去重就不能这样干;
used[i-1]=true;//说明同一树枝candidates[i - 1]使用过
used[i-1]=false;//说明同一树层candidates[i - 1]使用过
if(i>0&&candidates[i] == candidates[i - 1] && used[i - 1] == false)//说明同一树层使用过s
if(i>0&&candidates[i] == candidates[i - 1] && used[i - 1] == true)//说明同一树枝使用过
递归参数中有不变量(主函数要传进来的),也有变量,需要注意;
递归就是当前层做什么,下一层做什么,代码是相同的;
39.组合总和
写一个dfs函数,返回值为0,参数为位置u,与目标值的差值cnt,原数组;
每个数没有取值数量限制,所以循环中,把自己加上,每层都取自己就是多数量的情况;
class Solution {
public:
vector<int> V;
vector<vector<int>> ans;
int n;
void dfs(int u,int cnt,vector<int> candidates){
if(cnt==0){
ans.push_back(V);
return;
}
if(cnt<0||u==n) return;
for(int i=u;i<n;i++){
V.push_back(candidates[i]);
dfs(i,cnt-candidates[i],candidates);
V.pop_back();
}
return;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
ans.clear();
V.clear();
n=candidates.size();
dfs(0,target,candidates);
return ans;
}
};
40.组合总和II
10,1,2,7,6,1,5中17与71重复;
树层去重,先排个序11256710,举个例子,保证第一个1组合过的17,后面的1不组合了就;
用used标记数组来配合去重;注意向下延申的时候,可以取11,仅仅是树层维度1去过了,后面的1不取;
class Solution {
public:
vector<vector<int>> ans;
vector<int> V;
int n;
void dfs(int u,int cnt,vector<int> candidates,vector<bool> used){
if(cnt==0){
ans.push_back(V);
return;
}
if(cnt<0||u==n) return;
for(int i=u;i<n&&candidates[i]<=cnt;i++){
if(i>=1&&candidates[i]==candidates[i-1]&&used[i-1]==false) continue;
used[i]=true;
V.push_back(candidates[i]);
dfs(i+1,cnt-candidates[i],candidates,used);
used[i]=false;
V.pop_back();
}
return;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
ans.clear();
V.clear();
vector<bool> used(candidates.size(),false);
sort(candidates.begin(),candidates.end());
n=candidates.size();
dfs(0,target,candidates,used);
return ans;
}
};
131.分割回文串
分割回文串,其实跟组合数很像,首先画出递归搜索树;
for横向遍历,递归纵向遍历,函数参数为字符串s,起始位置u;
到头了,递归终止,把V放到ans;
判断是否回文,如果回文,放到V里,dfs下一段,记得回溯要恢复现场;
class Solution {
public:
vector<string> V;
vector<vector<string>> ans;
bool check(string s,int l,int r){
for(int i=l,j=r;i<j;i++,j--){
if(s[i]!=s[j]) return false;
}
return true;
}
void dfs(string s,int u){
if(u>=s.size()){
ans.push_back(V);
return;
}
for(int i=u;i<s.size();i++){
if(check(s,u,i)){
string t=s.substr(u,i-u+1);
V.push_back(t);
dfs(s,i+1);
V.pop_back();
}
}
return;
}
vector<vector<string>> partition(string s) {
V.clear();
ans.clear();
dfs(s,0);
return ans;
}
};