1.组合总和
题目链接/文章讲解: 代码随想录视频讲解:带你学透回溯算法-组合总和(对应「leetcode」力扣题目:39.组合总和)| 回溯法精讲!_哔哩哔哩_bilibili
代码:(未剪枝版 )
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& candidates,int target,int startIndex){
// 递归出口
if(target < 0){
return;
}
if(target == 0){
result.push_back(path);
return;
}
// 处理结点
for(int i = startIndex;i < candidates.size();i++){
target -= candidates[i];
path.push_back(candidates[i]);
backtracking(candidates,target,i);
path.pop_back();
target += candidates[i];
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size() == 0) return result;
backtracking(candidates,target,0);
return result;
}
};
代码:(剪枝版)
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& candidates,int target,int startIndex){
// 递归出口
if(target < 0){
return;
}
if(target == 0){
result.push_back(path);
return;
}
// 处理结点
// 这里加了判断:如果下一轮的target已经小于0了,就没有必要递归了
for(int i = startIndex;i < candidates.size() && target - candidates[i] >= 0;i++){
target -= candidates[i];
path.push_back(candidates[i]);
backtracking(candidates,target,i);
path.pop_back();
target += candidates[i];
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
if(candidates.size() == 0) return result;
sort(candidates.begin(), candidates.end()); // 涉及大小的剪枝必排序
backtracking(candidates,target,0);
return result;
}
};
思路:学到了这种元素数量不限制的取的解法
其实startIndex变量还是要有,因为我们求的是组合,要数层去重;并且这是在同一个数组里取数。
而数量不限,体现在我在递归里传入的starIndex的值是i,而不是i+1了。之前说过for循环管控每一层数的宽度;递归则管控数的深度。这样更改后,就可以在取完一个数的情况后,继续取这个数。实现如下图所示的效果:
关于剪枝的操作。这种涉及到总和大小的取值时。剪枝就要将给定的数组排序! 而且要注意给定的数组元素里全是正数,只有正数才越加越大。
这道题,我直接用目标总和target取减去每一个结点的数值了,看最后是否被减为0了。
2.组合总和2
题目链接/文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
代码:去重且剪枝过的
class Solution {
private:
vector<int> path;
vector<vector<int>> result;
void backtracking(vector<int>& candidates,int target,int startIndex,vector<bool>& used){
if(target < 0){
return;
}
if(target == 0){
result.push_back(path);
return;
}
for(int i = startIndex;i < candidates.size() && target - candidates[i] >= 0;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;
} // 数层去重
used[i] = true;
target -= candidates[i];
path.push_back(candidates[i]);
backtracking(candidates,target,i + 1,used);
path.pop_back();
target += candidates[i];
used[i] = false;
}
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<bool> used(candidates.size(),false);
sort(candidates.begin(),candidates.end());
backtracking(candidates,target,0,used);
return result;
}
};
思路:
这道题和上题的区别是:
每个元素只能使用一次——startIndex为i+1;
给定的元素有重复值,但是要去重后的结果:这里就是难点。首先去重是有两个维度的。
一个组合里可以有相同大小的不同元素,所以递归操作里不需要去重,即上图的同一个子树的数枝上不需要去重;不同组合不能相同,所以要在数层去重。
这里用一个数组used来记录,遇到的和前一个元素相同的情况(这里去重先去把给定数组排序)时,到底是和前一个相同元素是在同一个树枝里的,还是同一个树层里的。
在同一个树枝里:前一个元素已经加入到了path里,已经使用过了。
在同一个树层里:前一个元素没有加入到path里,没有被使用,这是独立的两个分支。
所以如果遇到这种树层里重复的情况,加个判断语句,直接continue(因为接下来的情况还有我们要收集的,所以只是跳过这一种情况,用continue)
3.分割回文串
代码随想录
视频讲解:带你学透回溯算法-分割回文串(对应力扣题目:131.分割回文串)| 回溯法精讲!_哔哩哔哩_bilibili
代码:
class Solution {
private:
vector<string> path;
vector<vector<string>> result;
bool isPalindrome(string& s,int start,int end){
while(start <= end){
if(s[start] != s[end]){
return false;
}
start++;
end--;
}
return true;
}
void backtracking(string& s,int startIndex){
// 递归出口
if(startIndex >= s.size()){
result.push_back(path);
return;
}
// 遍历结点
// 回文子串的范围是[startIndex,i]
for(int i = startIndex;i < s.size();i++){
// 在这里直接判断是否子串满足回文子串
if(isPalindrome(s,startIndex,i)){
string str = s.substr(startIndex,i - startIndex + 1);
path.push_back(str);
}else{
continue;
}
backtracking(s,i + 1);
path.pop_back();
}
}
public:
vector<vector<string>> partition(string s) {
if(s.size() == 0) return result;
backtracking(s,0);
return result;
}
};
思路:
关于substr函数:
这道题很巧妙地将startIndex作为了子串的开始下标,子串的范围可以表示为[startIndex,i]。
同时,本题将判断条件放在了for循环的里面来判断,来决定是否要执行下面的递归回溯操作。判断用了一个额外的函数来判断回文子串。
细节问题其实就这些。