1、基本思想
在搜索一幅图时,只需要用一个递归来遍历所有顶点。在访问其中一个顶点时:
- 将它标记为已访问
- 递归访问它的所有未标记的邻居顶点
DFS只会访问与起点s连通
的所有顶点(而不会访问其他顶点)。如果图是连通的,则所有顶点都会被访问到。
DFS中每条边都会被访问两次,且在第二次时总会发现这个顶点已经被标记过了。
DFS解决了单点连通性的问题,使得用例可以判定其他顶点和给定的起点是否连通。
2、算法实现
/*单点连通性*/
public class DepthFirstSearch{
private boolean[] marked; //使用一个boolean数组来记录和起点s连通的所有顶点
private int count; //与起点s连通的顶点数目
public DepthFirstSearch(Graph G, int s){
marked = new boolen[G.V()];
dfs(G, s);
}
private void dfs(Graph G, int v){
count++;
marked[v] = true;
for (int w : G.adj(v)){
if (!marked[w])
dfs(G, w);
}
}
public boolen marked(int v){
return marked[v]; }
public int count(){
return count; }
}
下图显示算法轨迹,起点为顶点0。
1、因为顶点2是0的邻接表的第一个元素且没有被标记过,dfs()标记该位置并且访问顶点2。
2、接下来,顶点0是2的邻接表的第一个元素且已经被标记过了,因此dfs()跳过了它。然后,顶点1是2的邻接表的第二个元素且没有被标记,调用dfs()来标记并访问顶点1。
3、对于顶点1的访问和前面的有所不同:因为它的邻接表中所有顶点(0和2)都已经被标记过,因此不需要再进行递归,方法从dfs(1)返回,下一条被检查的边时2-3,因此调用dfs()标记并访问顶点3.
。。。。后续同理
深度优先搜索可处理问题:
- 连通性。可以回答 “两个给定的顶点是否连通?” 或者 “找出图中所有连通分量” 等类似问题
- 单点路径。可以回答 “从s到目的顶点v是否存在一条路径?如果有找出这条路径” 等类似问题
3、单点路径问题
图处理算法的设计模式:将
图的表示
和实现
分离开来。为每个处理图的任务创建一个相应的操作类
,用例可以创建相应的对象来完成任务。
典型的用例程序会构造一幅图,将 图Graph 传递给实现了某个处理算法的类(作为其构造函数的参数),然后调用方法来获取图的各种性质。
根据上述设计模式,单点路径问题的API如下:
构造函数 Paths() 接受一个图和起点s作为参数,计算 s 到与s连通的所有顶点之间的路径。
在为起点s创建了Paths对象 后,用例可以调用 pathTo(v) 方法来遍历从s到v的路径上的所有顶点。
下述算法基于深度优先搜索实现了Paths操作类。它扩展了DepthFirstSearch,添加了一个实例变量 edgeTo[] 整型数组
。根据这个数组可以回溯找到从每个与s连通的顶点回到s的路径。它会记住每个顶点到起点的路径。
/* 使用深度优先搜索查找图中的路径*/
public class DepthFirstPaths {
private