0611刷题
回溯问题
基本模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
LeetCode 77. 组合
LeetCode 77. 组合
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
backtracking(1,n,k);
return result;
}
vector<vector<int>> result;
vector<int> vec;
void backtracking(int start,int n,int k)
{
if(vec.size()==k)
{
result.push_back(vec);
return;
}
for(int i=start;i<=n;++i)
{
vec.push_back(i);
backtracking(i+1,n,k);
vec.pop_back();
}
}
};
for循环每次从start开始遍历,然后用vec保存取到的节点i。
LeetCode 17. 电话号码的字母组合
LeetCode 17. 电话号码的字母组合
class Solution {
private:
const string letterMap[10] = {
"", // 0
"", // 1
"abc", // 2
"def", // 3
"ghi", // 4
"jkl", // 5
"mno", // 6
"pqrs", // 7
"tuv", // 8
"wxyz", // 9
};
public:
vector<string> result;
string s;
void backtracking(const string& digits, int index) {
if (index == digits.size()) {
result.push_back(s);
return;
}
int digit = digits[index] - '0'; // 将index指向的数字转为int
string letters = letterMap[digit]; // 取数字对应的字符集
for (int i = 0; i < letters.size(); i++) {
s.push_back(letters[i]); // 处理
backtracking(digits, index + 1); // 递归,注意index+1,一下层要处理下一个数字了
s.pop_back(); // 回溯
}
}
vector<string> letterCombinations(string digits) {
s.clear();
result.clear();
if (digits.size() == 0) {
return result;
}
backtracking(digits, 0);
return result;
}
};
终止条件就是如果index 等于 输入的数字个数(digits.size)了(本来index就是用来遍历digits的)
这个index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。
LeetCode 39. 组合总和
LeetCode 39. 组合总和
class Solution {
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
backtracking(candidates,target,0,0);
return result;
}
vector<vector<int>> result;
vector<int> vec;
void backtracking(vector<int>& candidates,int target,int sum,int start)
{
if(sum>target) return;
if(sum==target)
{
result.push_back(vec);
return ;
}
for(int i=start;i<candidates.size();++i)
{
vec.push_back(candidates[i]);
sum+=candidates[i];
backtracking(candidates,target,sum,i);
sum-=candidates[i];
vec.pop_back();
}
}
};
LeetCode 40. 组合总和 II
LeetCode 40. 组合总和 II
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
vector<bool> used(candidates.size(), false);
backtracking(candidates,target,0,0,used);
return result;
}
vector<vector<int>> result;
vector<int> vec;
void backtracking(vector<int>& candidates,int target,int sum,int start,vector<bool>& used)
{
if(sum>target) return;
if(sum==target)
{
result.push_back(vec);
return ;
}
for(int i=start;i<candidates.size()&& sum + candidates[i] <= target;++i)
{
// 要对同一树层使用过的元素进行跳过
//如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。此时for循环里就应该做continue的操作。
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false) continue;
vec.push_back(candidates[i]);
sum+=candidates[i];
used[i]=true;
backtracking(candidates,target,sum,i+1,used);
used[i]=false;
sum-=candidates[i];
vec.pop_back();
}
}
};
//1 1 2 5 6 7 10
与LeetCode 39. 组合总和套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
如果candidates[i] == candidates[i - 1]
并且 used[i - 1] == false
,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。此时for循环里就应该做continue的操作。
下面代码保证每个数字只使用一次:
backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1:这里是i+1,每个数字在每个组合中只能使用一次
LeetCode 131. 分割回文串
LeetCode 131. 分割回文串
class Solution {
private:
vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) {
// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
if (startIndex >= s.size()) {
result.push_back(path);
return;
}
for (int i = startIndex; i < s.size(); i++) {
if (isPalindrome(s, startIndex, i)) { // 是回文子串
// 获取[startIndex,i]在s中的子串
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
} else { // 不是回文,跳过
continue;
}
backtracking(s, i + 1); // 寻找i+1为起始位置的子串
path.pop_back(); // 回溯过程,弹出本次已经填在的子串
}
}
bool isPalindrome(const string& s, int left, int right) {
while(left<right)
{
if(s[left]!=s[right]) return false;
left++;
right--;
}
return true;
}
public:
vector<vector<string>> partition(string s) {
result.clear();
path.clear();
backtracking(s, 0);
return result;
}
};
注意点:判断回文串中使用 left和right,使得在回溯函数中可以直接在原string上判断是否回文串,然后用substr直接得到回文串。
bool isPalindrome(const string& s, int left, int right)
注意回溯过程中的continue,很关键。