回溯、递归、DFS方法
转自:https://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html1.
1.回溯解法框架
我觉得这个递归方法更好理解,迭代的方法我没太看懂。
1: int a[n]; 2: try(int i) 3: { 4: if(i>n) 5: 输出结果; 6: else 7: { 8: for(j = 下界; j <= 上界; j=j+1) // 枚举i所有可能的路径 9: { 10: if(fun(j)) // 满足限界函数和约束条件 11: { 12: a[i] = j; 13: ... // 其他操作 14: try(i+1); 15: 回溯前的清理工作(如a[i]置空值等); 16: } 17: } 18: } 19: }
以非常典型的,leetcode22题,生成括号为例:
class Solution(object): def generateParenthesis(self, N): ans = [] def backtrack(S = '', left = 0, right = 0): if len(S) == 2 * N: #给出解空间 ans.append(S) return if left < N:#这两个if判断都是剪枝 backtrack(S+'(', left+1, right) #扩展搜索节点 if right < left:#这里是关键 backtrack(S+')', left, right+1) backtrack() return ans
(1)针对所给问题,确定问题的解空间:
首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。 (对应if判断解)
(2)确定结点的扩展搜索规则 (确定往下搜索调用)
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。(if判断递归调用条件)
https://leetcode.com/articles/letter-combinations-of-a-phone-number/ 就像这道题,它只是一个简单的深度优先遍历然后得到所有的解,我觉得它关键是没有剪枝和回去的过程,只是遍历到最深的节点,然后返回了。
2.DFS和回溯的关系
https://www.1point3acres.com/bbs/thread-137584-1-1.html这个讲的挺好的。
特点:就是recursive call之后要退回到之前的一个状态。
回溯是DFS的一种,是它的子集。
回溯不保留树结构,也就是剪枝的意思。
回溯是一种更通用的算法。
深度优先搜索是与搜索树结构相关的回溯的一种特定形式。
3.递归和回溯
https://blog.csdn.net/fengchi863/article/details/80086915 这个补充的也挺好的。
- 递归是一种算法结构,回溯是一种算法思想。
- 一个递归就是在函数中调用函数本身来解决问题。
- 回溯就是通过不同的尝试来生成问题的解,有点类似于穷举,但是和穷举不同的是回溯会“剪枝”。
递归的一般过程:
void f() { if(符合边界条件) { /////// return; } //某种形式的调用 f(); }
回溯的一般过程:
void DFS(int 当前状态) { if(当前状态为边界状态) { 记录或输出 return; } for(i=0;i<n;i++) //横向遍历解答树所有子节点 { //扩展出一个子状态。 修改了全局变量 if(子状态满足约束条件) { dfs(子状态) } 恢复全局变量//回溯部分 } }
BFS和DFS相似。BFS显式用队列,DFS隐式用栈,即递归。
2020-4-3周五更新——————————————
1.回溯的三种问题方式
https://segmentfault.com/a/1190000006121957
1.有没有解
2.有多少个解
3.找出所有解是什么?
这三种分别对应着不通的返回值,当然框架都是一样的吧。
DFS它也是一种枚举的方法,暴力地走所有的路判断。
第一种,返回值是true/false。
第二种,求个数,设全局counter,返回值是void;求所有解信息,设result,返回值void。
第三种,设个全局变量best,返回值是void。
第一种:
boolean solve(Node n) { if n is a leaf node { if the leaf is a goal node, return true else return false } else { for each child c of n { if solve(c) succeeds, return true } return false } }
第二种:
void solve(Node n) { if n is a leaf node { if the leaf is a goal node, count++, return; else return } else { for each child c of n { solve(c) } } }
第三种:
void solve(Node n) { if n is a leaf node { if the leaf is a goal node, update best result, return; else return } else { for each child c of n { solve(c) } } }
好了,学习了。记住这个模板就ok了吧。