深度優先遍歷的主要思想是:
首先以一個未被訪問過的頂點作爲起始頂點,沿當前頂點的邊走到未訪問過的頂點:當沒有未訪問過的頂點時,則回到上一個頂點,繼續試探訪問別的頂點,直到所有的頂點都被訪問過。類似先序遍歷。
顯然,深度優先遍歷是沿着圖的某一條分支遍歷直到末端,然後回溯,再沿着另一條進行同樣的遍歷,直到所有的頂點都被訪問過爲止。
那麼,如何存儲一個圖呢?
常用的方法是使用一個二維數組來存儲。 圖的鄰接矩陣存儲法。
二維數組中的第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);//最少轉機次數
}
}