回溯算法理论基础

代码模板

1
2
3
4
5
6
7
8
9
void backtrack(int t) {
if (t > n) output(x);
else {
for (int i = f(n, t); i <= g(n, t); i++) {
x[t] = h(i);//当前解
if (constraint(t) && bound(t)) backtrack(t + 1);
}
}
}

回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!

因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度

回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。

大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

卡哥模板

1
2
3
4
5
6
7
8
9
10
11
12
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}

组合问题

每一层递归就是相当于一个for循环。

嵌套多少个for循环,就相当于递归多少次

经典错误,当时是我犯的错误

backtracking(n, k, startIndex + 1);应该是i + 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
//一维数组path,存放路径
//二维数组result,存放所有符合要求的path,组成结果集、
//也可以不定义成全局变量,但是定义成引用变量的话,递归函数里参数过多
vector<int> path;
vector<vector<int>> result;
void backtracking(int n, int k, int startIndex){
//递归终止条件
if(path.size() == k) {
result.push_back(path);
return;
}

//递归过程
for(int i = startIndex;i <= n;i++){
path.push_back(i);
backtracking(n, k, startIndex + 1);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return result;
}
};

正确答案

i <= n - (k - path.size()) + 1

常见剪枝思路就是缩小i的范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
//一维数组path,存放路径
//二维数组result,存放所有符合要求的path,组成结果集、
//也可以不定义成全局变量,但是定义成引用变量的话,递归函数里参数过多
vector<int> path;
vector<vector<int>> result;
void backtracking(int n, int k, int startIndex){
//递归终止条件
if(path.size() == k) {
result.push_back(path);
return;
}

//递归过程
for(int i = startIndex;i <= n;i++){
path.push_back(i);
backtracking(n, k, i + 1);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return result;
}
};

剪枝操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public:
//一维数组path,存放路径
//二维数组result,存放所有符合要求的path,组成结果集、
//也可以不定义成全局变量,但是定义成引用变量的话,递归函数里参数过多
vector<int> path;
vector<vector<int>> result;
void backtracking(int n, int k, int startIndex){
//递归终止条件
if(path.size() == k) {
result.push_back(path);
return;
}

//递归过程
for(int i = startIndex;i <= n - (k - path.size()) + 1;i++){
path.push_back(i);
backtracking(n, k, i + 1);
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return result;
}
};