算法-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个,然后往四个方向找下一个(递归)。

利用记忆数组记录是否访问过(防止回看)。

 

posted @ 2021-08-29 23:17  xuyv  阅读(86)  评论(0编辑  收藏  举报