根据描述,很容易实现图的深度优先搜索:
public class DepthFirstPaths {
private boolean[] marked; //标记已经访问过的结点
private int count;
public DepthFirstPaths(Graph G,int s) {//以s作为起始顶点深度优先遍历无向图G
marked = new boolean[G.V()];
dfs(G,s); //调用真正的深度优先遍历方法
}
//深度优先遍历
private void dfs(Graph G,int v) {
marked[v] = true;//标记当前顶点
count++;
for(int w: G.adj(v))
if(!marked[w]) dfs(G,w);//递归访问它所有没有标记过的相邻顶点
}
public boolean marked(int w) {return marked[w];}
public int count() {return count;}
}
深度优先遍历标记与起点连通的所有顶点所需的时间和顶点的度数之和成正比。
使用深度优先搜索查找图中路径:
只需很简单的修改深度优先遍历算法即可实现查找路径。添加一个实例变量edgeTo[]数组用来返回从每个与s相通的顶点返回s顶点的路径。搜索结果是一棵以起点为根节点的树,edgeTo[]是一棵由父节点组成的树。
修改深度优先遍历:
private void dfs(Graph G,int v){
marked[v] = true;
for(int w : G.adj(v))
if(!marked[w]){
edgeTo[w] = v; //只是多了这一条语句,保存路径中当前节点的上一节点
dfs(G,w);
}
}
获取s到v的路径:
public Iterable<Integer> pathTo(int v){
if(!hasPathTo(v)) return null;
Stack<Integer> path = new Stack<Integer>();//用栈保存路径
for( int x = v; x!=s;x = edgeTo[x]) //从顶点向下搜索
path.push(x); //路径上的结点进栈
path.push(s);
return path;
}
使用深度优先遍历得到从给定起点到任意标记顶点的路径所需的时间与路径长度成正比。
使用深度优先搜索找到图中所有的连通分量:
使用深度优先算法求解连通分量,递归第一次调用的参数是顶点0,它会标记所有与0连通的顶点。然后构造函数中的for循环会查找每个没有被标记的顶点并递归调用dfs()来标记和它相邻的所有顶点。
添加了一个id[]数组,同一个连通分量中的顶点的id[]值相同。
public CC(Graph G){
marked = new boolean[G.V()];
id = new int[G.V()];
for(int s = 0; s<G.V(); s++)
if(!marked[s]){
dfs(G,s);
count++; //连通分量个数加一
}
}
private void dfs(Graph G, int v){
marked[v] = true;
id[v] = count; //同一个连通分量下id[]值相同
for(int w:G.adj(v)
if(!marked[w])
dfs(G,w);
}
深度优先遍历的预处理使用的时间和空间与V+E成正比且可以在常数时间内处理图的连通性查询。
深度优先搜索和union-find算法比较:
理论上,深度优先算法比union-find快,因为它能够保证所需时间是常数而union-find算法不行。
实际上,union-find算法更快,因为它不需要完整的构造并表示一张图。更重要的是union-find算法是一种动态算法(我们在任何时候都能用接近常数的时间检查两个顶点是否连通,甚至在添加一条边的时候),但深度优先算法必须对图进行预处理。