图的广度优先搜索(Broad First Search),类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点。
一、什么是广度优先
就是尽可能一层一层找完,这一层找不到再找下一层。
二、思想
- 访问初始结点 v 并标记结点 v 为已访问。
- 结点 v 入队列
- 当队列非空时,继续执行,否则算法结束。
- 出队列,取得队头结点 u
- 查找结点 u 的第一个邻接结点 w
- 若结点 u 的邻接结点 w 不存在,则转到步骤 3;否则循环执行以下三个步骤:
6.1 若结点 w 尚未被访问,则访问结点 w 并标记为已访问。
6.2 结点 w 入队列
6.3 查找结点 u 的继 w 邻接结点后的下一个邻接结点 w,转到步骤 6。
结合上面的例子分析一下步骤:
- 从A开始,输出A,标记A已访问。把A加入到队列,转到A这一层,再移除A。
- 在A这一层,找到A的第一个结点B,输出B,标记B已访问,把B加入到队列。
- 在A这一层,找到B的邻接结点C,输出C,标记C已访问,把C加入到队列。
- 在A这一层,找到C的邻接结点没有,转到B这一层,再从队列中移除B。
- 在B这一层,找到B的第一个结点A,A已被访问;找到A的邻接节点C,C已被访问;找到C的邻接节点D,输出D,标记D已访问,把D加入到队列。
- 在B这一层,找到D的邻接节点E,输出E,标记E已访问,把E加入到队列。
- 在B这一层,找到E的邻接节点,没有,转到C这一层,再从队列中移除C。
- 在C这一层,找到C的第一个结点A,A已被访问;找到A的邻接节点B,B已被访问;.....直到走到C这一层的尽头,转到D这一层,再从队列中移除D。
- 在D这一层,......直到走到C这一层的尽头,转到E这一层,再从队列中移除E。
- 在E这一层,......直到走到E这一层的尽头,列表为空,结束。
三、代码实现
这里面有一部分深度优先的代码(已经被注释掉了),深度优先看http://t.csdnimg.cn/F9Qa6。
为什么重载了bfs方法呢?为了防止是非连通图。
package Graph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
public class Graph {
private ArrayList<String> vertexList; //存储顶点集合
private int [][]edges; //存储图对应的邻接矩阵
private int numOfEdges; //边的个数
//是否被访问
private boolean[] isvisited;
public static void main(String[] args) {
int n=5; //节点个数
String Vertexs[]={"A","B","C","D","E"};
Graph graph=new Graph(n);
//循环添加点
for(String vertex: Vertexs){
graph.insertVertex(vertex);
}
//添加边
graph.insertEdge(0,1,1);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,1);
graph.insertEdge(1,3,1);
graph.insertEdge(1,4,1);
//显示邻接矩阵
graph.showGraph();
// System.out.println("深度优先");
// graph.dfs();
System.out.println();
System.out.println("广度优先");
graph.bfs();
}
//构造器
public Graph(int n){
edges=new int[n][n];
vertexList=new ArrayList<String>(n);
numOfEdges=0;
isvisited=new boolean[5];
}
//得到第一个邻接节点的下标w
public int getFirstNeighbor(int index){
for(int j=0;j<vertexList.size();j++){
if(edges[index][j]>0){
return j;
}
}
return -1;
}
//返回下一个邻接节点的下标
public int getNextNeighbor(int v1,int v2){
for(int j=v2+1;j<vertexList.size();j++){
if(edges[v1][j]>0){
return j;
}
}
return -1;
}
//广度优先
public void bfs(boolean[]isvisited,int i){
int u; //这一行的结点
int w; //邻接结点
LinkedList<Integer> queue=new LinkedList<>();
System.out.print(getValueByIndex(i)+"->");
isvisited[i]=true;
queue.addLast(i);
while(!queue.isEmpty()){
u=(Integer)queue.removeFirst();
w=getFirstNeighbor(u);
while(w!=-1){
if(!isvisited[w]) {
System.out.print(getValueByIndex(w) + "->");
isvisited[w] = true;
queue.addLast(w);
}
w=getNextNeighbor(u,w);
}
}
}
public void bfs(){
for(int i=0;i<getNumOfVertex();i++){
if(!isvisited[i]) {
bfs(isvisited, i);
}
}
}
/*
//深度优先
public void dfs(boolean []isvisited,int i){
System.out.printf(getValueByIndex(i)+"->");
isvisited[i]=true;
int w =getFirstNeighbor(i);
while(w!=-1){
if(!isvisited[w]){
dfs(isvisited,w);
}
w=getNextNeighbor(i,w);
}
}
public void dfs(){
isvisited=new boolean[vertexList.size()];
for(int i=0;i<getNumOfVertex();i++){
if(!isvisited[i]){
dfs(isvisited,i);
}
}
}
*/
//图中常用的方法
//返回节点个数
public int getNumOfVertex(){
return vertexList.size();
}
//返回边的个数
public int getNumOfEdges(){
return numOfEdges;
}
//数字对应的点 0->'A' 1->'B'
public String getValueByIndex(int i){
return vertexList.get(i);
}
//返回v1和v2的权值
public int getWeight(int v1,int v2){
return edges[v1][v2];
}
//显示图对应的矩阵
public void showGraph(){
for(int []link:edges){
System.out.println(Arrays.toString(link));
}
}
//插入节点
public void insertVertex(String vertex){
vertexList.add(vertex);
}
//添加边
public void insertEdge(int v1,int v2,int weight){
edges[v1][v2]=weight;
edges[v2][v1]=weight;
numOfEdges++;
}
}