Leetcode: 39 组合总和
基本思路
本题没有组合数字的要求,只有对组合总和的要求,因此返回条件有两个,等于sum的时候收集结果,如果大于sum了就直接跳过。
- 组合没有数量要求
- 元素可以重复拾取
这题的难点在于可以反复取值。因此对于回溯的设置需要技巧。本题的关键在于回溯的输入
traceback(candidates, target, sum, i);//关键点:不用i+1了,表示可以重复读取当前的数
class Solution {
private:vector<vector<int>> result;vector<int> vec;void traceback(vector<int>& candidates, int target, int sum, int idx){if(target < sum) return;if(sum == target){result.push_back(vec);return;}for(int i = idx; i < candidates.size(); i++){sum += candidates[i];vec.push_back(candidates[i]);traceback(candidates, target, sum, i);sum -= candidates[i];vec.pop_back();}}
public:vector<vector<int>> combinationSum(vector<int>& candidates, int target) {traceback(candidates, target, 0,0);return result;}
};
剪枝优化
如果我们事先对candidate进行从小到大的排序的话。当下一层 sum + candidates[i]已经比sum大的时候,就可以提前剪枝了。在求和问题中,先排序再求和是一个常用手段。相关代码如下:
//排序代码
sort(candidates.begin(), candidates.end());
//循环逻辑
for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++)
时间复杂度O(n * 2^n)
空间复杂度O(target)
Leetcode: 40 组合总和II
本题与上题的区别在于,元素不能重复使用。没有思路,参考下述题解。
代码随想录
核心的内容是跳过重复的元素。这题一定要先排序,如果一个数层上面元素被重复使用的,就需要跳过。并且这道题必须先对数组进行排序,不然无法进行跳过操作。
// 要对同一树层使用过的元素进行跳过if (i > startIndex && candidates[i] == candidates[i - 1]) {continue;}
class Solution {
private:vector<vector<int>> result;vector<int> vec;void traceback(vector<int>& candidates, int target, int sum, int idx, vector<bool>& used){if(sum == target){result.push_back(vec);return;}for(int i = idx; i < candidates.size() && sum + candidates[i] <= target; i++){if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {continue;}sum += candidates[i];vec.push_back(candidates[i]);used[i] = true;traceback(candidates, target, sum, i + 1, used);sum -= candidates[i];used[i] = false;vec.pop_back();}}
public:vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<bool> used(candidates.size(), false);sort(candidates.begin(), candidates.end());traceback(candidates, target, 0,0,used);return result;}
};
Leetcode: 131 分割回文串
基本思路
- 分割问题
- 判断回文的问题
递归思路
- 输入:字符串、一个记录分割位置的idx
- 结束条件:切割位置到达字符末尾;
- 递归逻辑:判断idx到i的区间内字符串是不是回文,如果是就加入当前的结果,继续向下寻找。回文的判断条件使用双指针,一前一后进行比较。
class Solution {
private:vector<vector<string>> result;vector<string> vec;void traceback(string s, int idx){if(idx >= s.size()){//终止条件result.push_back(vec);return;}for(int i = idx; i < s.size(); i++){if(ishuiwen(s, idx, i)){//如果是回文的话就加入vecvec.push_back(s.substr(idx, i - idx +1));}else continue;//不是就跳过traceback(s, i + 1);//进入下一层递归,因为不能重复切割,所以是i+1vec.pop_back();}}bool ishuiwen(string s, int start, int end) {//双指针判断回文for (int i = start, j = end; i < j; i++, j--) {if (s[i] != s[j]) {return false;}}return true;}
public:vector<vector<string>> partition(string s) {traceback(s , 0);return result;}
};
困难点
- 切割问题可以抽象为组合问题
- 如何模拟那些切割线
- 切割问题中递归如何终止
- 在递归循环中如何截取子串
- 如何判断回文