深度优先搜索
什么是深度优先搜索(DFS)
前一篇描述了,搜索图的算法之一的广度优先搜索(breadth-first search,BFS),而另外一种搜索图的算法,则是深度优先搜索(depth-first search,DFS)。
深度优先搜索算法(DFS)会从第一个指定的顶点开始遍历图,沿着路径直到这条路径的最后一个顶点被访问了,再原路折回探索吓一跳路径。
深度优先搜索是”先深度后广度地访问顶点”的图搜索算法。
深度优先搜索(DFS)全过程
例如用下面的图结构,从顶点A开始搜索,直到搜索到顶点G。
可以知道,顶点A的相邻顶点有顶点B、顶点C、顶点D,这三个顶点,将它们视为下一步的候选顶点,按顺序逐个深度递归搜索。由于顶点B是最先探索到的相邻顶点,则从顶点B开始深度探索,移动到顶点B。
到达顶点B,可以获知它的相邻顶点是E和F,则将它们视为下一步的候选顶点,由于顶点E是最先探索到的相邻顶点,则继续从顶点E开始深度探索,移动到顶点E。
到达顶点E,可以获知它的相邻顶点是K,则将它视为下一步的候选顶点,继续从顶点K开始深度探索,移动到顶点K。
到达顶点K,由于顶点K没有路径下的相邻顶点,则原路折返知道有未探索顶点的顶点B,探索顶点F。
接下来不断重复相同的操作:
- 到达顶点F,由于顶点F没有路径下的相邻顶点,则原路折返知道有未探索顶点的顶点A,探索顶点C。
- 到达顶点C,可以获知它的相邻顶点是H,则将它视为下一步的候选顶点,继续从顶点H开始深度探索,移动到顶点H。
- 到达顶点C,可以获知它的相邻顶点是H,则将它视为下一步的候选顶点,继续从顶点H开始深度探索,移动到顶点H。
- 到达顶点H,可以获知它的相邻顶点是G,则将它视为下一步的候选顶点,继续从顶点G开始深度探索,移动到顶点G。
- 到达顶点G,找到需要到达的目标,结束搜索。
实现深度优先搜索(DFS)
代码流程
- 为了标记图的顶点,我们需要提供一个表示顶点颜色的常量对象。
- 白色(WHITE):表示该顶点还没有被访问
- 灰色(GREY):表示该顶点被访问过,但并未被探索过
- 黑色(BLACK):表示该顶点被访问过且被完全探索过
1 |
|
- 并且提供一个辅助方法,用于初始化每个顶点颜色。
1 |
|
接着便是深度优先搜索算法的完整代码思路:
- 声明广度优先搜索算法的函数-depthFirstSearch,接收两个参数,分别是graph(要遍历的图实例)和callback(回调函数)
- 在函数内获取传入图实例(graph)的顶点列表、邻接表,并将图的顶点列表中的所有顶点初始化颜色为白色(表示该顶点还没有被访问)
- 遍历图实例的所有顶点,如果当前遍历的顶点项是白色(表示该顶点还没有被访问),则递归访问这个顶点,递归顶点使用的函数-depthFirstSearchVisit,传入函数需要的四个参数要访问的顶点(u)、图所有顶点的颜色对象(color)、图的邻接表(adjList)和回调函数(callback),它内部的代码流程:
- 把当前访问的顶点项(u),标识为灰色(该顶点被访问过,但并未被探索过)
- 如果有传入回调函数,则执行该回调函数,回调函数会将这个顶点作为入参方法。(执行该函数输出已访问过的顶点)
- 获取当前访问顶点项(u)的邻接表,如果这个顶点的邻接表不为空,则遍历这个顶点的邻接表中的顶点,遍历时,如果该邻接表的顶点是白色,则以该邻接表的顶点,进行递归访问。
- 这个顶点的邻接表探索完后,将这个顶点标识为黑色(已被访问且被完全探索)
代码实现
1 |
|
代码用例
我们初始化一个图,用于测试深度优先搜索。
1 |
|
深度优先搜索算法(DFS)还可以做更多
深度优先搜索算法的工作原理了解之后,它还可以做更多事情,比如使用它来搜索图顶点的初次发现时间、完成访问时间,或者图中某个顶点的前溯点。
下面是一段利用DFS探索图实例中每个顶点的初次发现时间、完成访问时间和前溯点。
代码实现
1 |
|
使用DFS实现拓扑排序
什么是拓扑排序
当我需要编排一些任务或步骤的执行顺序时,这称为拓扑排序(topological sorting,英文亦写作 topsort 或是 toposort)。
例如,当开始学习一门计算机科学课程,在学习某些知识之前得按顺序完成一些知识储备(你不可以在上 算法 I 课程前先上算法 II 课程)。
当我们在开发一个项目时,需要按顺序执行一些步骤。例如,首先从客户那里得到需求,接着开发客户要求的东西,最后交付项目。你不能先交付项目再去收集需求。
代码用例
1 |
|
完整代码实现
附上上述所有代码的完整实现
1 |
|