如下图所示的图,采用深度优先搜索(DFS)进行遍历:
描述一下问题的解决方案:
在访问其中一个顶点时:
1) 将它标记为已访问;
2) 递归的访问它的所有没有被标记过的邻居顶点。
选择某一顶点为遍历的起始顶点,即可遍历图(连通图)的所有顶点。
boolean[] marked = new boolean[g.V()]; //marked数组大小为图的顶点个数。
public void dfs(Graph g, int v){
marked[v] = true;
count++;
System.out.println("output : " + v);
for(int w : g.adj(v)) { //adj[v]为与顶点v相连接的顶点集合。
if(!marked[w]) {
dfs(g,w);
}
}
}
以dfs(g,4)为例,看一看其调用轨迹:
遍历的顺序为从上到下,圆圈表示该顶点已经访问过,矩形表示还未访问。
非递归的实现有一定的难度,首先能想到的是使用栈来保存,何时入栈和出栈需要花时间来想一想。
如果是进栈前就打印:
public void dfsNoIterationWrong(Graph g, int v,ArrayList<Integer> list){
marked[v] = true;
stack.push(v);
System.out.println(v);
while(!stack.isEmpty()) {
int top = stack.pop();
for(int w : g.adj(top)) {
if(!marked[w]) {
marked[w] = true;
System.out.println(w);
stack.push(w);
}
}
}
if(allMarked(marked)){
System.out.println(list);
}
}
结果: 0 1 2 5 6 4 3
这样写是错误的。这样既不是深度也不是广度搜索。第一轮0的邻接节点1、2、5、6的输出顺序没错,接下来会先打印顶点6的邻接结点4,因为此时6位于栈顶。广度优先搜索正确的方法是使用队列。
正确的算法如下:public void dfsNoIteration(Graph g, int v,ArrayList<Integer> list){
System.out.println(v);
marked[v] = true;
stack.push(v);
boolean hasUnvisitedNeigbor;
while(!stack.isEmpty()) {
int top = stack.peek(); //先不出栈
hasUnvisitedNeigbor = false;
for(int w : g.adj(top)) {
if(!marked[w]) {
System.out.println(w);
marked[w] = true;
stack.push(w);
hasUnvisitedNeigbor = true;
break;
}
}
if(!hasUnvisitedNeigbor) { //表示top的邻接顶点已经全部处理完
stack.pop();
}
}
if(allMarked(marked)){
System.out.println(list);
}
}
输出结果:0 1 2 5 3 4 6