算法-DFS 回溯 递归
2. 先画出图,再写代码
3. 写先简单的dfs,再剪枝。
虽然不是最秀的,但至少能让你看得懂! - 括号生成 - 力扣(LeetCode)
backtracking = DFS + 回溯 + 剪枝
把方法命名为dfs比较好理解。dfs一直到有用解或者不可解。有用解,返回true(也可回溯); 不可解,回溯。
回溯和DP:回溯一般用于组合问题,DP一般求最优解问题。
所以回溯的解一般是各种组合,如全排列、组合总和等。
回溯的思想是树(或者森林?),遍历+递归。最经典的就是全排列。
回溯的重点在剪枝,可以快速去掉不可能的节点。
参考:https://guides.codepath.com/compsci/Backtracking
核心原理还是递归(归纳),即将问题分解为 1. 已知问题(结束条件) 2. 原问题的子问题
这样定义好1和2,计算机就可以自动地求解问题。(核心在于假设有解)
回溯:
def backtrack(...)
for choice in list:
make choice
backtrack(path, list)
...
可以看到核心是递归,然后加了一层for迭代。
经典题目:
1. 电话号码的字母组合
采用递归思想,将问题分解为第一个电话号码, 和剩下电话号码的字母组合子问题的解,的组合问题。
(遗传算法的搜索空间组合可以用这个)
2. 括号生成
问题抽象:括号生成,有效的生成的括号在于,在从左往右遍历时,左括号的数量总是大于右括号的数量。
因此,使用left_num作为核心判断条件。
使用递归,递归的思想是假设有解。
if (left_num) add (; right_num ++; left_num--
if (right_num) add ); right --; left_num不变。
3. 组合总和
还是使用递归。在确定第一个数的情况下,子问题就是target - first的组合总和。终止条件为target最终被减为0
dfs(target, combine, idx)
4. 子集
一样是回溯、递归。
选择,或者不选择当前i。
t.push_back(i)
dfs(i+1...)
t.pop_back(i)
dfs(i+1...)
5. 单词搜索
回溯 递归
首先for循环对每一个点遍历,在for循环内递归。
递归的是每一个点是否match当前第k个字幕。第一次递归是第0个,然后往四个方向找下一个(递归)。
利用记忆数组记录是否访问过(防止回看)。