回溯算法思想
一、回溯模板时刻铭记于心
// backtrace: // if 满足条件: // 添加到结果集 // return // for elem in 可选: // 选择 // backtrace() // 退回选择
二、回溯思想
### 回朔法的思想: 回朔法的重要思想在于: 通过枚举法,对所有可能性进行遍历。 但是枚举的顺序是 一条路走到黑,发现黑之后,退一步,再向前尝试没走过的路。直到所有路都试过。因此回朔法可以简单的理解为: 走不通就退一步的方枚举法就叫回朔法。而这里回退点也叫做回朔点。
### 回朔关键点 通过分析发现,回朔法实现的三大技术关键点分别是:
- 一条路走到黑 ----dfs
- 回退一步
- 另寻他路
### 关键点的实现 那么如何才能用代码实现上述三个关键点呢?
- for 循环
- 递归
#### 解释如下
-
for循环的作用在于另寻他路: 你可以用for循环可以实现一个路径选择器的功能,该路径选择器可以逐个选择当前节点下的所有可能往下走下去的分支路径。 例如: 现在你走到了节点a,a就像个十字路口,你从上面来到达了a,可以继续向下走。若此时向下走的路有i条,那么你肯定要逐个的把这i条都试一遍才行。而for的作用就是可以让你逐个把所有向下的i个路径既不重复,也不缺失的都试一遍
-
递归可以实现一条路走到黑和回退一步: 一条路走到黑: 递归意味着继续向着for给出的路径向下走一步。 如果我们把递归放在for循环内部,那么for每一次的循环,都在给出一个路径之后,进入递归,也就继续向下走了。直到递归出口(走无可走)为止。 那么这就是一条路走到黑的实现方法。 递归从递归出口出来之后,就会实现回退一步。
因此for循环和递归配合可以实现回朔: 当递归从递归出口出来之后。上一层的for循环就会继续执行了。而for循环的继续执行就会给出当前节点下的下一条可行路径。而后递归调用,就顺着这条从未走过的路径又向下走一步。这就是回朔
说了这么多,回朔法的通常模板是什么呢? 递归和for又是如何配合的呢?
三、例子
class Solution { public: vector<int> res; vector<vector<int>> ans; vector<vector<int>> combinationSum(vector<int>& candidates, int target) { backtracting(candidates, 0, 0, target); return ans; } void backtracting(vector<int> &candidates, int index, int sum,int targer) { if (sum > targer) return; if (sum == targer) { ans.push_back(res); return; } for (int i = index; i < candidates.size(); i++) { res.push_back(candidates[i]); sum += candidates[i]; backtracting(candidates, i, sum, targer); //回溯,i访问完了,从i+1这个新路径从新开始 res.pop_back(); sum -= candidates[i]; } } };