Leetcode - 39:组合总和:
这道题我觉得是中规中矩与前面几个题目基本大差不差,就是需要注意的是元素可以无限制使用,这样我们只需要更改递归调用solve函数时的start传入的值不再是i + 1,而是i本身,然后在函数的判断终止条件上再加上一条如果当前sum已经大于target就直接返回没必要继续递归下去。
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
void solve(int target, int start, int n, int sum, vector<int>& candidates){
if(sum == target){
res.push_back(path);
return;
}
if(sum > target) return;
for(int i = start; i < n; i++){
path.push_back(candidates[i]);
solve(target, i, n, sum + candidates[i], candidates);
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
solve(target, 0, candidates.size(), 0, candidates);
return res;
}
};
Leetcode - 40 组合总和II
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
vector<bool> used;
void solve(int target, int sum, int start, int n, vector<int>& element){
if(sum == target){
res.push_back(path);
return;
}
if(sum > target || start == n) return;
for(int i = start; i < n; i++){
if(i > 0 && element[i] == element[i - 1] && used[i - 1] == true){
used[i] = true;
continue;
}
path.push_back(element[i]);
solve(target, sum + element[i], i + 1, n, element);
used[i] = true;
path.pop_back();
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
int sum = 0;
used.assign(candidates.size(), false);
sort(candidates.begin(),candidates.end());
solve(target, sum, 0, candidates.size(), candidates);
return res;
}
};
这里错误的原因在于树层去重并未完善,并未深刻体会到回溯的过程:
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
vector<bool> used;
void solve(int target, int sum, int start, int n, vector<int>& element){
if(sum == target){
res.push_back(path);
return;
}
if(sum > target || start == n) return;
for(int i = start; i < n; i++){
// 由于回溯所以前面遍历过得同一元素已经被回溯到false状态
if(i > 0 && element[i] == element[i - 1] && used[i - 1] == false){
continue;
}
path.push_back(element[i]);
// 一旦被加入数组就代表该元素已经被使用过了,就立刻将其标记为true
used[i] = true;
solve(target, sum + element[i], i + 1, n, element);
// 回溯状态
used[i] = false;
path.pop_back();
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
int sum = 0;
used.assign(candidates.size(), false);
sort(candidates.begin(),candidates.end());
solve(target, sum, 0, candidates.size(), candidates);
return res;
}
};
树层去重,遍历元素直到当前元素与上一个元素不一致的时候便不再跳过,这里我们为什么要跳过而不是标记后再跳过,因为每个节点都有一个回溯的过程标记玩节点后还是需要回溯状态的,所以我们直接跳过可以省去设两个步骤。
Leetcode - 131 分割回文子串:
这道题的难点在于分割的方法,这道题在看过图解思路后就会清晰该如何去做,我们依照回溯三部曲一一进行:首先是判断传入的参数:这里我们需要做的操作是分割子串所以要对原字符串进行分割,所以我们用到了原字符串s,因为我们只是取出子串并未对原串进行修改所以我们保证传入的是静态的字符串。然后还有一个start变量进行标记我们分割的位置,接着第二步是函数终止条件,这里当我们的start遍历到了字符串的末尾我们也就相应得到了一个分割的序列所以我们将该path序列传入到res中直接返回。第三步确定单层函数:我们分割字符串从第一个字符开始往后遍历,先确定当前分割的子串是否为回文串,如果是就传入path数组中,接着再递归对后续字符进行分割,分割完后回溯,将元素弹出。
class Solution {
public:
vector<string> path;
vector<vector<string>> res;
void solve(const string& s, int start){
if(start > s.size() - 1){
res.push_back(path);
return;
}
for(int i = start; i < s.size(); i++){
if(ishuiwen(start, i, s)){
string str = s.substr(start, i - start + 1);
path.push_back(str);
solve(s, i + 1);
path.pop_back();
}else{
continue;
}
}
}
bool ishuiwen(int start, int end, const string& s){
for(int i = start, j = end; i < j; i++, j--){
if(s[i] != s[j]) return false;
}
return true;
}
vector<vector<string>> partition(string s) {
solve(s, 0);
return res;
}
};