回溯理论基础、组合问题
回溯理论基础
回溯能解决的问题
回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案
回溯如何穷举:
横向遍历for循环,纵向遍历backtracking(递归),一般来说,搜索叶子节点就是找的其中一个结果。
注意:如何避免重复,利用startIndex
回溯法模板
回溯函数模板返回值以及参数
回溯算法中函数返回值一般为void。
再来看一下参数,因为回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。
回溯函数终止条件
既然是树形结构,那么我们在讲解二叉树的递归 (opens new window)的时候,就知道遍历树形结构一定要有终止条件。所以回溯也有要终止条件。什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点 了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
回溯搜索的遍历过程
在上面我们提到了,回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
模板伪代码
cpp
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77. 组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
思路: 参照回溯模板即可,重点搞明白 startIndex去重、如何回溯
cpp
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循环
for(int i = startIndex; i <= n; i++){
//startIndex = i+1 用于横向遍历去重,纵向下一层时从i+1进行横向遍历
path.push_back(i);
//纵向遍历,递归
backtracking(n, k, i+1);
path.pop_back(); //回溯,撤销处理结果
}
}
vector<vector<int>> combine(int n, int k) {
if(k > n) return result;
backtracking(n, k, 1);
return result;
}
};