深度优先遍历的主要思想是:
首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点:当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问过。类似先序遍历。
显然,深度优先遍历是沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止。
那么,如何存储一个图呢?
常用的方法是使用一个二维数组来存储。 图的邻接矩阵存储法。
二维数组中的第i行第j列表示的就是顶点i到顶点j是否有边。 1 表示有边,自己到自己(即i等于j)设为0.
package BFS_DFS;
import java.util.*;
public class DFS {
public static int[] book=new int[101];
public static int sum,n;
public static int[][] e=new int[101][101];
public static void dfs(int cur){
int i;
System.out.println(cur);
sum++;//每访问一个顶点,sum就加1
if(sum==n){
return;//所有的顶点都已经访问过则直接退出
}
for(i=1;i<=n;i++){//从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连
//判断当前顶点cur到顶点i是否有边,并判断顶点i是否已访问过
if(e[cur][i]==1 && book[i]==0){
book[i]=1;//标记顶点i已经访问过
dfs(i);//从顶点i再出发继续遍历
}
}
return;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int i,j,m,a,b;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else
e[i][j]=99999999;//假设99999999为正无穷
}
}
for(i=1;i<=m;i++){
a=sc.nextInt();
b=sc.nextInt();
e[a][b]=1;
e[b][a]=1;//这里是无向图,需要将e[b][a]=1
}
sc.close();
book[1]=1;//从1号城市出发,标记1号顶点已访问
dfs(1);
}
}
广度优先遍历的主要思想是:
首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有的顶点都被访问过,遍历结束,类似层次遍历。
过程如下:首先以一个未被访问过的顶点作为起始顶点,比如以1 号顶点作为 起点,将1号顶点放入到队列中,然后将与1 号顶点相邻的未被访问过的顶点 即2 号、3 号和5 号顶点依次再放入到队列中,接下来再将 2 号顶点相邻的未被访问过的顶点4 号放入队列中。遍历结束。
package BFS_DFS;
import java.util.*;
public class BFS {
public static void main(String[] args) {
// TODO Auto-generated method stub
int i,j,n,m,a,b,cur;
int[] book=new int[101];
int[][] e=new int[101][101];
int[] que=new int[10001];
int head,tail;
LinkedList<Integer> queue=new LinkedList<>();
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else e[i][j]=99999999;
}
}
for(i=1;i<=m;i++){
a=sc.nextInt();
b=sc.nextInt();
e[a][b]=1;
e[b][a]=1;//无向图,
}
sc.close();
//队列初始化
head=1;
tail=1;
que[tail]=1;
tail++;
book[1]=1;//标记1 号顶点已访问
//当队列不为空的时候循环
while(head<tail){
cur=que[head];//当前正在访问 的顶点编号
for(i=1;i<=n;i++){
//判断从顶点cur到顶点i是否有边,并判断顶点i是否已经访问过
if(e[cur][i]==1 && book[i]==0){
//如果顶点cur 到顶点i有边,且顶点i没有访问过,则将顶点i入队列
que[tail]=i;
tail++;
book[i]=1;
}
//如果tail大于n,则表明所有顶点都已经被访问过
if(tail>n)
break;
}
//注意:不要忘记当一个顶点扩展结束后, head++,然后才能继续向下扩展
head++;
}
for(i=1;i<tail;i++){
System.out.println( que[i]);
}
}
}
eg: 求出 1 号城市 到 5 号城市的最短路径。
可以用一个二维矩阵来存储任意两个城市之间的路程。比如
e[1][2]的值为2表示从1 号城市到2 号城市的路程为2 公里。
package BFS_DFS;
import java.util.*;
public class DFS_1 {
static int min=99999999;
static int[] book=new int[101];
static int[][] e=new int[101][101];
static int n;
//cur 表示当前所在城市的编号,dis 是当前已经走过的路程
public static void dfs(int cur, int dis){
int j;
//如果当前走过的路程已经大于之前找到的最短路,则没有必要再往下尝试了,立即返回
if(dis>min)return;
if(cur==n){//判断是否到达了目标城市
if(dis<min)min=dis;//更新最小值
return;
}
for(j=1;j<=n;j++){//从1 号城市到 n号城市依次尝试
//判断当前城市cur到城市j是否有路,并判断城市j是否在已经走过的路径中
if(e[cur][j] != 99999999 && book[j] ==0){
book[j]=1;//标记城市j已经在路径中
dfs(j,dis+e[cur][j]);//从城市j出发,继续寻找目标城市
book[j]=0;//之前一步探索完毕之后,取消对城市j的标记
}
}
return;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int i,j,m,a,b,c;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else
e[i][j]=99999999;
}
}
for(i=1;i<=m;i++){
a=sc.nextInt();
b=sc.nextInt();
c=sc.nextInt();
e[a][b]=c;//有向图
}
book[1]=1;//从1 号城市出发,标记1 号城市已经在路径中
dfs(1,0);//1 表示当前城市所在的城市编号,0 表示当前已经走过的路程
System.out.println(min);
}
}
图就是有 N个顶点和 M条边组成的集合。
广度优先搜索更加适用于所有边的权值相同的情况。
eg: 最少转机次数
还是使用邻接矩阵来存储图。
package BFS_DFS;
import java.util.*;
class Note{
int x;//城市编号
int s;//转机次数
Note(int x, int s){
this.x=x;
this.s=s;
}
}
public class BFS_1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Note[] que=new Note[2501];
int[][] e=new int[51][51];
int[] book=new int[51];
int head,tail;
int i,j,n,m,a,b,cur,start,end,flag=0;
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
start=sc.nextInt();
end=sc.nextInt();
//初始化二维矩阵
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i==j)e[i][j]=0;
else
e[i][j]=99999999;
}
}
//读入城市之间的航班
for(i=1;i<=m;i++){
a=sc.nextInt();
b=sc.nextInt();
e[a][b]=1;
e[b][a]=1;//无向图
}
//队列初始化
head=1;
tail=1;
//从start号城市出发,将start号城市加入队列
que[tail].x=start;
que[tail].s=0;
tail++;
book[start]=1;//标记start号城市已在队列中
//当队列不为空时循环
while(head<tail){
cur=que[head].x;//当前队列中首城市的编号
for(j=1;j<=n;j++){
if(e[cur][j]!=99999999&& book[j]==0){
que[tail].x=j;
que[tail].s=que[head].s+1;//转机次数加1
tail++;
book[j]=1;
}
//如果到达目标城市,退出循环
if(que[tail-1].x==end){
flag=1;
break;
}
}
if(flag==1)
break;
head++;//注意: 不要忘记当一个点扩展结束后,head++才能继续扩展
}
System.out.println(que[tail-1].s);//最少转机次数
}
}