一、注意点
深度优先搜索算法最为明晰的解释就是使用走迷宫,一条路走到黑,这个例子进行讲述。其本质问题是使用递归的方式将所有的可能性都进行遍历,逐个核对,以得到要求的结果。
以下给出核心问题,可以对照第二部分的模板进行阅读。
DFS的核心问题是:
- 走到什么时候结束?
- 如何遍历?
- 多少条路需要走到黑?
问题1
第一个问题即收敛条件问题,也就是边界值问题。当走到边界值处(例如走到了迷宫某一条路的尽头)将会根据题目的要求进行评价,以决定该路径是否是我们要找的那一条路。(在迷宫问题中即判断是否到达了出口,如果到达了出口,就将该正确路径返回,不是出口就返回前序点继续遍历)。
问题2
第二个问题是关于如何使用递归进行遍历,其本质是对于每一个可能性进行遍历。
如果在背包问题(一个容量有限的背包,一堆有体积的物体,是否放入背包)中,可能性有选择和不选择两种,那么就只需要使用两次DFS(),分别对选择和不选择两种情况进行遍历。但是对于岛屿问题(二维棋盘中有水和岛屿,进行统计)或者多分叉迷宫问题,就会有多个可能性。如果要不产生错误,需要对所有的可能性进行归纳,依次遍历。
问题3
第三个问题即剪枝问题。剪枝即剪去不必要的枝蔓,也就是剪去不需要进行遍历的路径。比如在背包问题中,假设背包容积是10,那么选择物体的体积之和就不可以超过10。即只有在当前已经装入的物体体积和加上下一个待遍历的物体体积之和小于10的情况下,才进行遍历,这样就可以极大的节省计算资源。如果剪枝操作设计得当,在遍历到边界时可以直接默认为满足题目要求情况,直接输出。
二、模板
//函数类型是int 还是void根据题目要求
void DFS(要遍历的路径比如数组,标志元素index等)
{
//第一部分解决第一个问题,在哪里停下,即设定遍历边界
if(到达边界值,比如数组遍历到头)
{
//判断条件(可选)
if(物体体积和小于背包容积)
{
//输出或者返回值
}
//如果不满足题目要求
{
return;//返回上一层遍历其他的路径
}
}
//第二部分包含第二个和第三个问题,可以选择直接遍历,
//也可以选择进行优化先进行剪枝操作再遍历
if(判断是否达到了剪枝的条件)//可选
{
DFS()//需要剪枝判断的路径,比如背包问题中选择该物体
}
DFS()//不需要剪枝的路径,比如背包问题中不选择该物体
}
在以上模板中,尤其要注意第二部分DFS()。
- DFS()不同的路径的顺序问题
- DFS()内参数的问题,要写正确。
三、适用题型
由以上解释可以看出,深度优先搜索DFS适用于以下几种情况:
- 涉及通过多个选择,得到一条路径或者一个计划的问题
- 题目给出了一维数组或者二维数组的问题,尤其是二维数组问题要格外注意,可能使用DFS。
- 要求对题目所给的二维数组进行修改的问题
- 涉及二叉树的问题。