39.组合总和
自己写的代码:
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
int sum=0;
void backtracking(vector<int>& candidates,int target,int startIndex){
if(sum>target) return;
if(sum==target){
res.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size();i++){
path.push_back(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,i);
sum-=candidates[i];
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates,target,0);
return res;
}
};
关键点:
- 通过和来控制树的深度
剪枝优化:
剪枝通常要在for循环里面做文章,上面的代码,对于sum大于target的情况,其实也是进入了下一层递归,只是下一层递归会判断sum>target而直接返回。
如果已经知道下一层的sum会大于target,就没有必要进入下一层递归了
对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历。
优化后代码:
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
int sum=0;
void backtracking(vector<int>& candidates,int target,int startIndex){
if(sum==target){
res.push_back(path);
return;
}
for(int i=startIndex;i<candidates.size()&&sum+candidates[i]<=target;i++){
path.push_back(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,i);
sum-=candidates[i];
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
backtracking(candidates,target,0);
return res;
}
};
40.组合总和II
自己AC的代码:
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
int sum=0;
void backtracking(vector<int>& candidates, int target,int index){
if(sum==target){
res.push_back(path);
return;
}
for(int i=index;i<candidates.size()&&sum+candidates[i]<=target;){
path.push_back(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,i+1);
sum-=candidates[i];
path.pop_back();
int j=i;
while(i<candidates.size()&&candidates[j]==candidates[i]){
i++;
}
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
backtracking(candidates,target,0);
return res;
}
};
去重的逻辑(自己想的):通过画树辅助分析,在回溯的时候,弹出去的元素,与下一层循环要加进来的元素不能相等,这样答案就不会重复(当然最一开始先对数组进行排序)
所以for循环的条件不能简单加1,而是要通过循环找到第一个与当前元素不同的位置。
代码随想录讲解:
用一个bool数组来定义每个下标的元素是否使用过,我们要去重的维度是树层,而树枝上可以有重复元素(实质上是不同下标但数值相同的元素)
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
if (sum == target) {
result.push_back(path);
return;
}
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
// 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) {
continue;
}
sum += candidates[i];
path.push_back(candidates[i]);
used[i] = true;
backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
used[i] = false;
sum -= candidates[i];
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<bool> used(candidates.size(), false);
path.clear();
result.clear();
// 首先把给candidates排序,让其相同的元素都挨在一起。
sort(candidates.begin(), candidates.end());
backtracking(candidates, target, 0, 0, used);
return result;
}
};
个人觉得自己的思路更好理解。
131.分割回文串
class Solution {
public:
vector<vector<string>> res;
vector<string> path;
bool isHuiWen(string s,int i,int j){
while(i<=j){
if(s[i]!=s[j]) return false;
i++;j--;
}
return true;
}
void backtracking(string s,int startIndex){
if(startIndex>=s.size()){
res.push_back(path);
return;
}
for(int i=startIndex;i<s.size();i++){
if(isHuiWen(s,startIndex,i)){
string str=s.substr(startIndex,i-startIndex+1);
path.push_back(str);
}else{
continue;
}
backtracking(s,i+1);
path.pop_back();
}
}
vector<vector<string>> partition(string s) {
backtracking(s, 0);
return res;
}
};
- 终止条件:当切割到最后一个位置时,结束。体现在代码中就是开始位置在最后一个元素之后
- 子串区间:子串的区间是
[startIndex,i]
- 首先判断这个子串是不是回文,如果是回文,就加入在
vector<string> path
中,path用来记录切割过的回文子串。