回溯 (DFS) 算法框架

46. 全排列

51. N 皇后

 

其实在本篇文章之前,已经有过关于 DFS 题型的总结:排列/组合/子集 问题秒杀所有岛屿题目(DFS)

「DFS 算法」和「回溯算法」其实是同一个概念,本质上都是一种暴力穷举算法

「DFS 算法」其实就是在树的遍历过程中增加了「决策」。关于对树的遍历的详细理解,可以参考 二叉树--纲领篇 中的相关部分

框架的提出

下面我们来说点 DFS 算法的核心内容。我们先以树为前提,便于理解

当我们站在回溯树的一个节点上,你只需要思考 3 个问题:

基于上述三个问题,给出相对应的伪代码:

全排列问题

对于全排列问题,我们可以抽象成一颗三叉树,如下图所示:

3

我们现在需要得到所有的全排列组合,无非就是得到所有从根节点到叶子结点的路径!

问题一:由于这是一颗抽象出来的树,叶子节点并没有和树一样的特征,如何判断到达了叶子节点??

回答一:根据路径的长度判断是否结束!!

下面我们先不考虑其他的情况,把所有的路径遍历出来,代码如下:

最后运行的结果:

问题二:对于上述结果,存在元素重复使用的情况,如何避免这种情况呢??(如上图红色标注的分支均为不符合条件的情况)

回答二:新增一个used[]数组,记录元素使用情况!!

修改后的代码如下:

最后运行结果如下:

N 皇后问题

首先我们来分析一下。对于每一行,我们可以有 n 个选择,即任选一个格放下棋子,只要满足要求即可;同时,我们需要做 n 次这样的选择,即存在 n 行,每一行选择一个格子放棋子

N 皇后的决策树如下图所示:

3

很不巧的是,n = 3 时,没有一种情况时符合的。不过这不重要!!!(如上图红色标注的分支均为不符合条件的情况)

问题一:用什么数据来表示棋盘呢???

回答一:二维数组。.表示未放棋子;Q表示放了棋子

问题二:如何判断当前行某一格放下棋子是满足要求的呢??

回答二:具体代码如下:

现在,我们来实现核心代码部分:

岛屿问题

关于岛屿相关的问题,可以去 传送门 详细查看

岛屿问题是一个图的问题,本质也是使用 DFS 算法。在这里只简单的解释一下是如何套用我们的模版滴滴滴!!

这里把岛屿的模版 copy 过来了!!如下所示:

我们来分析一下这个框架的结构,看是否与本文最开始给出的框架保持一致

首先来看下面两行代码:

这两行代码实质是给出了结束递归的 base case。类似于「满足结束条件」时,保存结果,并返回

这一行代码是处理当前节点的逻辑代码

这四行代码表示可以从 4 个方向进行递归搜索

至于这里为什么没有最后的撤销操作?原因是我们需要保存修改后的数据,在后续需要用到的,所以不需要撤销!!