前言
图的遍历与树的遍历相似,也是从某个顶点出发,沿着某条搜索路径对图的每个顶点做且仅做一次访问。
深度优先搜索遍历(Depth First Search,DFS)
深度优先遍历类似于树的前序遍历,选则任意一个顶点v作为初始出发点,首先访问出发点v并将其标记为已访问过;然后搜索v的每个邻接点w。若w未被访问,则从w开始出发继续进行深度优先遍历(即继续访问w的邻接点),直到所有与v路径相同的结点都访问到;若此时图中仍有顶点没被访问,就选则没被访问的顶点开始出发,重复以上步骤,直至图中所有顶点访问完。
则图1的深度优先遍历的顶点访问序列为:a,b,d,h,e,c,f,g
对图进行深度优先遍历时,按访问顶点的先后次序得到的顶点序列称为图的深度优先遍历序列,或简称为DFS序列
广度优先搜索遍历(Breadth First Search,BFS)
广度优先搜索遍历类似于树的按层级遍历。
其基本思想就是:首先访问出发点v,接着访问v的所有未被访问过的邻接点v1,v2...,并将其标记为已访问过,在依次访问v1,v2...的邻接点并标记,以此类推;直至所有顶点都被访问到。
那么图1的以a为出发点的广义优先搜索遍历序列为:a,b,c,d,e,f,g,h
在广度优先遍历中,先被访问的顶点,其邻接点也先被访问,即符合队列的性质:先进先出。
(a)
图2的邻接矩阵为a,以4为出发结点的两种遍历序列如下:
DFS:4,1,2,0,3
BFS:4,0,1,3,2
总结
如算法名称那样,深度优先搜索所遵循的搜索策略是尽可能“深”地搜索树。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前(子结点)探索,在探索过程中,一旦发现原来的选择不符合要求,就回溯至父亲结点重新选择另一结点,继续向前探索,如此反复进行,直至求得最优解。
减少节点数,思想:尽可能减少生成的节点数
定制回溯边界,思想:定制回溯边界条件,剪掉不可能得到最优解的子树
在很多情况下,我们已经找到了一组比较好的解。但是计算机仍然会义无返顾地去搜索比它更“劣”的其他解,搜索到后也只能回溯。为了避免出现这种情况,我们需要灵活地去定制回溯搜索的边界。
在深度优先搜索的过程当中,往往有很多走不通的“死路”。假如我们把这些“死路”排除在外,不是可以节省很多的时间吗?打一个比方,前面有一个路径,别人已经提示:“这是死路,肯定不通”,而你的程序仍然很“执着”地要继续朝这个方向走,走到头来才发现,别人的提示是正确的。这样,浪费了很多的时间。针对这种情况,我们可以把“死路”给标记一下不走,就可以得到更高的搜索效率。
对于状态数很多时,广度优先搜索可以采用循环队列或动态链表来处理,对于这两种搜索算法,其主要区别如下表:
遍历方式 | 深度优先搜索遍历 | 广度优先搜索遍历 |
所用数据结构 | 栈 | 队列 |
一般优化 | 最优性剪枝 可行性剪枝 | Hash判重 双向搜索 |