理论基础
回溯法也称作回溯搜索法,是一种搜索的方式。回溯和递归联系很紧密,只要有递归就会有回溯。另外,回溯法的效率很低,是穷举所有的可能,属于暴力搜索。
回溯法解决的问题:
- 组合问题;
- 切割问题;
- 子集问题;
- 排列问题;
- 棋盘问题;
回溯法模板
- 回溯函数模板返回值以及参数: 函数一般起名为backtracking, 返回值一般是void。需要的参数根据逻辑确定;
- 终止条件: 搜索到叶子结点,找到答案并把这个答案存放起来,并结束本层递归;
- 回溯搜索的遍历过程: 回溯法一般是在集合中递归搜索,集合的大小构成树的宽度,递归的深度构成树的深度。
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
LeetCode 77. 组合
解题思路
本题要求返回所有可能的k个数的组合,元素不能重复。把组合问题抽象成如下树形结构:
可以看出,这棵树刚开始时集合是1、2、3、4, 从左向右4取数,不重复。第一次取1,集合变成2、3、4。因为k=2,只需要再取一个数就可以了,分别取2、3、4。
对照回溯算法的模板, for循环中是集合的大小,可选择的范围是不断进行收缩的。另外,在下一次递归的时候,集合也发生了变化,需要调整左区间的值,这里需要借助一个参数startIndex 对应集合的左区间。
终止条件是收集到k个数,并将k个数的集合进行保存。
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
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;
}
};