Day24_回溯算法
1. 回溯算法理论基础
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
回溯是递归的副产品,只要有递归就会有回溯。
回溯法,一般可以解决如下几种问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
组合: 不强调元素顺序。
排列: 强调元素顺序。
2. 组合
77. 组合
思路:
由于是组合,没有顺序,为了确保不重不漏,每次在区间
[startIndex, n]
之间寻找 k 个数,startIndex 每次 +1,代码如下
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) { // 找到满足条件的答案,记录并返回
ans.push_back(path);
return ;
}
for (int i = startIndex; i <= n; i++) {
path.push_back(i); // 操作
backtracking(n, k, i + 1); // 递归
path.pop_back(); // 回溯
}
}
public:
vector<vector<int>> combine(int n, int k) {
ans.clear();
path.clear();
backtracking(n, k, 1);
return ans;
}
};
3. 组合(优化)
77. 组合
思路:
可以对上一问进行剪枝优化,当待查询的区间小于 k 时,一定找不到答案,不用在这些区间中查询
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
ans.push_back(path);
return ;
}
// 区间内的数个数加上已经被选了的path中的数的个数,要能够达到 k 个数
for (int i = startIndex; i <= (n - k + 1 + path.size()); i++) { // k <= (n - i + 1) + path.size()
path.push_back(i);
backtracking(n, k, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> combine(int n, int k) {
ans.clear();
path.clear();
backtracking(n, k, 1);
return ans;
}
};
4. 组合总和 III
216. 组合总和 III
思路:
在
[1,9]
里找 k 个数加和等于 n
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
void backtracking(int k, int n, int startIndex, int sum) {
if (path.size() == k || sum >= n) {
if (path.size() == k && sum == n) { // 满足条件的答案
ans.push_back(path);
return ;
} else return ;
}
for (int i = startIndex; i <= 9; i++) {
path.push_back(i);
backtracking(k, n, i + 1, sum + i);
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum3(int k, int n) {
path.clear();
ans.clear();
backtracking(k, n, 1, 0);
return ans;
}
};
剪枝:
减两处:
- 由于是从前往后查询,当出现
sum > n
时,此后的 sum 一定大于 k,不用再继续往后找了- 同前面的组合问题,当区间的长度加上 path 的长度不足 k 时,一定不满足条件,不必继续查询
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
void backtracking(int k, int n, int startIndex, int sum) {
if (path.size() == k || sum >= n) {
if (path.size() == k && sum == n) { // 满足条件的答案
ans.push_back(path);
return ;
} else return ; // 剪枝1
}
for (int i = startIndex; i <= (9 - k + 1 + path.size()); i++) { // 剪枝2
path.push_back(i);
backtracking(k, n, i + 1, sum + i);
path.pop_back();
}
}
public:
vector<vector<int>> combinationSum3(int k, int n) {
path.clear();
ans.clear();
backtracking(k, n, 1, 0);
return ans;
}
};