提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、LeetCode39. 组合总和
题目链接:LeetCode39. 组合总和
这道题利用经典的回溯算法便可轻松解决。
首先题目要求我们找出一个数组中可以使数字和为目标数的所有组合,那么我们需要一个一维数组来存放每种组合的数,还要一个二维的数组来存放所有的组合数组。
然后就是递归,我们需要传入三个参数,分别是题目给出的数组和目标数,还有每层递归时一维数组中每个元素存放的起始位置。每放入一个数同时更新数组中所有数值的和之后,继续递归下一位,如果此时所有数值的和等于目标数时,将数组中记录的数存放二维数组结果集当中,然后结束递归或者和大于目标数时也结束递归(题目给的数值时大于1的正数,故和只会越加越大)。
注意递归结束之后回溯时,要将上一层加上的数值减掉,还要删掉存放在路径上的数。
代码如下:
class Solution {
private:
vector<vector<int>>result;//存放结果集
vector<int>path;//记录每一组数
int sum = 0;//记录每组数的和
public:
void travel(vector<int>& candidates, int target, int startx) {
if(sum >= target) {
if(sum == target) {
result.push_back(path);
}
return;
}
for(int i = startx; i < candidates.size(); i++) {//数组中的每个数从有效起始位置开始存放,递归完后要收回
path.push_back(candidates[i]);
sum += candidates[i];
travel(candidates, target, i);
sum -= candidates[i];
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
travel(candidates, target, 0);
return result;
}
};
二、LeetCode40.组合总和II
题目链接:LeetCode40.组合总和II
这道题的解法更上一题类似,都要用到回溯法。
不过因为这道题要求不能包含重复元素,所以还要对数组集进行去重操作。
首先因为题目所给的数组中可能含有重复元数,所以先对其进行排序,让重复的元素排在一起以便更好的进行去重。
然后跟上面一样,递归,传参(题目给出的数组,目标数,和每个路径点的有效起始位置)、递归结束条件。
但是在处理每层循环中路径点时,进行避免相同数值多次记录在同一路径点的操作。
具体做法是,记录上一层循环中记录在路径点上的数值,如果这次循环中遇到了同样的数值,则直接进行下一层循环,直到出现不同的数值,才开始递归。
class Solution {
private:
int sum = 0; //记录路径上的数值和
int lastint = 0;//记录上一层循环中路径点上的数值,以便排重
vector<vector<int>>result;
vector<int>path;
public:
void travel(vector<int>&candidates, int target, int startx) {
if (sum >= target) {
if (sum == target) {
result.push_back(path);
}
return;
}
for (int i = startx; i < candidates.size(); i++) {
if(lastint == candidates[i]) continue;//如果上一层循环中删掉的数值等于这次的数值,则会重复,所以直接跳过这一次的操作。
sum += candidates[i];
path.push_back(candidates[i]);
travel(candidates, target, i + 1);
sum -= candidates[i];
lastint = path.back();
path.pop_back();
}
}
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());//为了更好的排重先对数组进行排序
travel(candidates, target, 0);
return result;
}
};
三、LeetCode131.分割回文串
题目链接:131.分割回文串
分割回文串这道题还是比较难的,而且同样要用递归和回溯。
比较困难的是不容易对字符串的分割区间进行处理,而且还要判断区间内的字符串是否是回文。
我们先来处理单层递归逻辑,这里我们要用到传进来的参数(切割的起始位置),然后用for循环依次列举切割的末尾,每次循环,要判断切割区域内的字符串是否是回文(这里可以另写一个函数来判断回文,也可以直接再里面判断),如果不是回文的话就继续下一个i的循环,如果是,就要把该回文放进方案路径里面,然后递归下一个回文的切割(切割点的起始位置为i+1),当然递归之后还要进行回溯(就是收回路径上的最后一个回文)。
以下是代码:
class Solution {
private:
vector<vector<string>>result;//二维数组收集所有结果集
vector<string>path;//记录每组分割方案
int sum = 0;//计算记录在每组分割方案中的字符个数,用来判断是否已将所有字符分割完
public:
bool ishuiwen(string &s, int left, int right) {//判断在分割区域内的字符串是否为回文
while(left < right) {
if(s[left] != s[right]) return false;
else {
left++;
right--;
}
}
return true;
}
void backtraving(string &s, int startx) {
if(startx >= s.size()) {
result.push_back(path);
return;
}
for(int i = startx; i < s.size(); i++) {//从标记的起始位置切割不同长度的字符串然后继续递归,再回溯
if(ishuiwen(s, startx, i)) {
path.push_back(s.substr(startx,i - startx + 1));
} else continue;
backtraving(s, i + 1);
path.pop_back();
}
return;
}
vector<vector<string>> partition(string s) {
backtraving(s, 0);
return result;
}
};
总结
以上就是今天要讲解的算法内容。
通过写博客对每到题有了深刻的理解,真的收获了很多!