题目链接:leetcode 39. 组合总和
文章讲解:代码随想录 39. 组合总和讲解
视频讲解:Leetcode:39. 组合总和讲解
思路和解法
题目:
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
想法:
给的数组无重复元素,每个元素可以任意使用,递归时就不要从下一个数字开始尝试,而是从当前数字继续开始尝试加进去。如果要剪枝一定要先排序,只有排序才能认为当前数字加进来已经超过target,后面就不用再遍历了。
class Solution {
public:
//存结果与路径
vector<vector<int>> result;
vector<int> path;
//递归+回溯
void backTracking(vector<int>& candidates, int target, int startIndex, int sum) {
//终止条件
if (sum > target) {
return ;
}
if (sum == target) {
result.push_back(path);
return ;
}
//递归处理逻辑
//横向遍历
//这里还可以做剪枝 剪枝需要先排序
// for (int i = startIndex; i < candidates.size(); i++) {
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
sum += candidates[i];
path.push_back(candidates[i]);
backTracking(candidates, target, i, sum);
path.pop_back();
sum -= candidates[i];
}
return ;
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
//初始化
result.clear();
path.clear();
//剪枝需要先排序
sort(candidates.begin(), candidates.end());
//递归回溯
backTracking(candidates, target, 0, 0);
return result;
}
};
题目链接:leetcode 40.组合总和II
文章讲解:代码随想录 40.组合总和II讲解
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II
思路和解法
题目:
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
想法:
给的数组有重复元素,每个元素只能用一次,所以递归时要从下一个数字开始尝试加入,另外一定要先排序,因为要去重,排序以后才方便去重,去重的逻辑是:比如[1,1,1,2,3],第一个选1以后,第二个可以选第二个1,也可以选第3个1,但是假如target=4,这样导致递归到[1,1,2]可能是第一个1和第二个1,也可能是第一个1和第三个1,所以递归时再选择第二个数字时要跳过第三个1这种情况,就判断当前层for循环遍历到的数字和前一个数字是否相同,相同跳过。注意第二个1在第二层不用跳过。
class Solution {
public:
//组合问题一定要这两个
vector<vector<int>> result;
vector<int> path;
//回溯+递归
void backTracking(const vector<int>& candidates, const int target, int sum, int startIndex) {
//终止条件
if (sum == target) {
result.push_back(path);
return ;
}
if (sum > target) {
return ;
}
//递归处理逻辑
for (int i = startIndex; i < candidates.size(); i++) {
if (i > startIndex && candidates[i] == candidates[i - 1]) {
continue;
}
sum += candidates[i];
path.push_back(candidates[i]);
backTracking(candidates, target, sum, i + 1);
path.pop_back();
sum -= candidates[i];
}
return ;
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
result.clear();
path.clear();
sort(candidates.begin(), candidates.end());
backTracking(candidates, target, 0, 0);
return result;
}
};
题目链接:leetcode 131.分割回文串
文章讲解:代码随想录 131.分割回文串讲解
视频讲解:131.分割回文串
思路和解法
题目:
给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
想法:
分割线类似选数字,要提前规定好分割线的意义,本次使用的就是[startIndex, i]这样的左闭右闭区间,这道题目的终止条件就是分割线把整个字符串全部割完了同时每个子串都满足回文。回文串判断用双指针。
class Solution {
public:
//回溯+递归
vector<string> path;
vector<vector<string>> result;
//判断是否为回文串
bool isValid(string& s) {
for (int i = 0, j = s.size() - 1; i < j; i++, j--) {
if (s[i] != s[j]) return false;
}
return true;
}
void backTracking(const string& s, int startIndex) {
//终止条件 截取的子串为[startIndex, i] 所以startIndex超过字符串长度为终止条件
if (startIndex >= s.size()) {
result.push_back(path);
return ;
}
//递归处理逻辑 截取的子串为[startIndex, i],判断截取出来的子串是否为回文串,如果是继续向后分割,如果不是跳过当前分割
for (int i = startIndex; i < s.size(); i++) {
//substr(起始下标,长度)
string tempString = s.substr(startIndex, i - startIndex + 1);
//判断是否为回文串
if (isValid(tempString)) {
path.push_back(tempString);
backTracking(s, i + 1);
path.pop_back();
} else {
continue;
}
}
return ;
}
vector<vector<string>> partition(string s) {
path.clear();
result.clear();
backTracking(s, 0);
return result;
}
};