一、算法描述
广度优先搜索算法使用了一个队列来保存所有已经被标记过但其邻接表还未被检查过的顶点。先将起点加入队列,然后重复以下步骤直到队列为空:
- 取队列中的下一个顶点 v 并标记它;
- 将与 v 相邻的所有未被标记过的顶点加入队列。
bfs不是递归的。不像递归中隐式使用的栈,它显式地使用了一个队列。和深度优先搜索一样,它的结果也是一个数组 edgeTo[],也是一棵用父链接表示的根结点为 s 的树。它表示了 s 到每个与 s 连通的顶点的最短路径。
性质
对于从 s 可达的任意顶点 v,广度优先搜索都能找到一条从 s 到 v 的最短路径(没有其他从 s 到 v 的路径所含的边比这条路径更少)。
二、算法实现
本算法只针对邻接表表示的图结构进行广度优先搜索,其图的创建代码参考上一篇文章无向图的创建(java实现),本算法依赖上一篇文章所建立图。
package graph;
import java.util.*;
/**
* 宽度优先搜索
* @author hh
*/
public class WidthFirstSearch {
/**
* 图对象
*/
private Graph graph;
/**
* 访问标记列表
*/
private boolean[] marked;
/**
* 用于记录起点s到顶点v的路径
*/
private int[] edgeTo;
/**
* 起点编号
*/
private int startVer;
private WidthFirstSearch() {
}
public WidthFirstSearch(Graph graph, int startVer) {
this.graph = graph;
this.startVer = startVer;
marked = new boolean[graph.getVertices()];
Arrays.fill(marked,Boolean.FALSE);
edgeTo = new int[graph.getVertices()];
Arrays.fill(edgeTo,0);
}
public void wfs(){
//用于记录已经标记的但临界点没有访问的顶点
Queue<Integer> queue = new LinkedList<Integer>();
//访问顶点并入队
System.out.print(this.startVer + " ");
marked[startVer] = true;
queue.add(this.startVer);
//如果队列不为空访问其临界点
while (!queue.isEmpty()){
int v = queue.remove();
for(Integer w : graph.adj(v)){
//如果没有访问,则访问并添加到队列中
if(!isMarked(w)){
System.out.print(w + " ");
marked[w] = true;
edgeTo[w] = v;
queue.add(w);
}
}
}
}
/**
* 查找起点s到w路径集合
*
* @param w : 顶点编号
* @return :起点s到w路径集合
*/
public Collection<Integer> pathTo(int w){
return GraphUtils.pathTo(this.edgeTo,this.startVer,w);
}
private boolean isMarked(int v){
return marked[v];
}
}
package graph;
import java.util.Collection;
import java.util.LinkedList;
/**
* 图的工具类
* @author hh
*/
public class GraphUtils {
/**
* 打印图中起点到终点路径
*
* @param edgeTo : 路径数组
* @param s : 起点编号
* @param w : 终点编号
* @return : 路径顶点列表
*/
public static Collection<Integer> pathTo(int[] edgeTo, int s, int w){
LinkedList<Integer> path = new LinkedList<>();
path.addFirst(w);
int v = edgeTo[w];
while (v != s){
path.addFirst(v);
v = edgeTo[v];
}
path.addFirst(s);
return path;
}
}