14.回溯算法
回溯法介绍
回溯与递归相辅相成,在递归函数的下面。
属于纯暴力搜索的算法,本质是穷举。
可以解决的问题类型包括:
组合、切割、子集、排列、棋盘类的问题
回溯法解决的问题都可以抽象为树形结构。因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。
回溯法的模板
回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。
carl总结的回溯算法模板框架如下
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77组合问题
题目描述
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
解题思路
这是典型的组合问题,可以用回溯法求解。
代码实现
class Solution {
public:
vector<vector<int>>res;
vector<int>path;
void backtracking(int n,int k,int startIndex)
{
if(path.size()==k)//当path中放的元素个数符合组合要求,就写入结果,退出递归
{
res.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 res;
}
};
总结
对于组合问题,采用回溯的方法暴力搜索所有满足的方案。在暴力搜索时,利用for循环对集合本身遍历,实现横向搜索,利用递归实现对纵向符合要求的方案元素深度进行搜索,搜到以后利用回溯返回for循环对应的根节点,开启下一次搜索。
溯的方法暴力搜索所有满足的方案。在暴力搜索时,利用for循环对集合本身遍历,实现横向搜索,利用递归实现对纵向符合要求的方案元素深度进行搜索,搜到以后利用回溯返回for循环对应的根节点,开启下一次搜索。