深度优先搜索最初是因为迷宫游戏而诞生的。在一个迷宫中,有一个入口和一个出口,其中只有一条路径能从入口到达出口。在走迷宫的时候,每次将走过的地方进行标记,遇到死胡同的时候可以沿着进来的路线后退,找到新的没走过的拐角再尝试新的路线。这种方法的效率很高,因为每个地方只需要走过一次即可。其实,这就是深度优先搜索。
深度优先搜索的目标就是系统化地遍历整个图,让算法的效率更高。
应用
深度优先搜索有几个非常典型的应用:
-
找出源顶点能到达的所有顶点
-
找出两个顶点之间的路径
-
判断两个顶点是否连通
基本思想
深度优先搜索的步骤如下:
-
将v标记为已拜访
-
迭代拜访与v相邻的没有拜访过的所有顶点
设计模式
和图论有关的算法非常多,相应的数据结构也非常多。那么,为了不让Graph这个类变得太复杂,所以需要将图论的算法放在另外一个单独的对象中,这样两个类之间的耦合度低一些,编码的时候也会更加好用。因此在后续的章节中都会采用这样的设计模式,使得每个算法都会有一个单独的类。
代码
import java.util.Stack;
/**
* Created by caipeichao on 14-6-10.
*/
public class DFS {
private boolean[] visited;
private int[] edgeTo;
private int s;
/**
* @param G 图
* @param s 遍历的起点
*/
public DFS(Graph G, int s) {
this.s = s;
// 所有的顶点都没有访问过
visited = new boolean[G.V()];
// 从哪个顶点来的?
edgeTo = new int[G.V()];
// 开始深搜
dfs(G, s);
}
private void dfs(Graph G, int v) {
visited[v] = true;
for (int w : G.adj(v)) {
if (!visited[w]) {
edgeTo[w] = v;
dfs(G, w);
}
}
}
public boolean hasPathTo(int v) {
return visited[v];
}
public Iterable<Integer> pathTo(int v) {
if(!hasPathTo(v)) return null; // 注意:这句话不要忘记了
Stack<Integer> result = new Stack<Integer>();
while (v != s) { // 注意:到达源点的时候就应该退出循环
result.add(v);
v = edgeTo[v];
}
return result;
}
}
复杂度
简单地说复杂度和参与运算的边数成正比。所谓参与运算的边就是指源点能到达的边。