廣度優先搜尋與深度優先搜尋
廣度優先搜尋(寬度優先搜尋,BFS)和深度優先搜尋(DFS)演算法的應用非常廣泛,本篇文章主要介紹BFS與DFS的原理、實現和應用。
深度優先搜尋
圖的深度優先搜尋(Depth First Search),和樹的先序遍歷比較類似。
它的思想:假設初始狀態是圖中所有頂點均未被訪問,則從某個頂點v出發,首先訪問該頂點,然後依次從它的各個未被訪問的鄰接點出發深度優先搜尋遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。 若此時尚有其他頂點未被訪問到,則另選一個未被訪問的頂點作起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。
顯然,深度優先搜尋是一個遞迴的過程。
下面進行說明:
第1步:訪問A。
第2步:訪問(A的鄰接點)C。在第1步訪問A之後,接下來應該訪問的是A的鄰接點,即"C,D,F"中的一個。但在本文的實現中,頂點ABCDEFG是按照順序儲存,C在"D和F"的前面,因此,先訪問C。
第3步:訪問(C的鄰接點)B。在第2步訪問C之後,接下來應該訪問C的鄰接點,即"B和D"中一個(A已經被訪問過,就不算在內)。而由於B在D之前,先訪問B。
第4步:訪問(C的鄰接點)D。在第3步訪問了C的鄰接點B之後,B沒有未被訪問的鄰接點;因此,返回到訪問C的另一個鄰接點D。
第5步:訪問(A的鄰接點)F。 前面已經訪問了A,並且訪問完了"A的鄰接點B的所有鄰接點(包括遞迴的鄰接點在內)";因此,此時返回到訪問A的另一個鄰接點F。
第6步:訪問(F的鄰接點)G。
第7步:訪問(G的鄰接點)E。
因此訪問順序是:A -> C -> B -> D -> F -> G -> E。
當然,上圖是基於無向圖,具體的程式碼在文章後面實現。
廣度優先搜尋
廣度優先搜尋演算法(Breadth First Search),又稱為"寬度優先搜尋"或"橫向優先搜尋",簡稱BFS。
它的思想是:從圖中某頂點v出發,在訪問了v之後依次訪問v的各個未曾訪問過的鄰接點,然後分別從這些鄰接點出發依次訪問它們的鄰接點,並使得“先被訪問的頂點的鄰接點先於後被訪問的頂點的鄰接點被訪問,直至圖中所有已被訪問的頂點的鄰接點都被訪問到。如果此時圖中尚有頂點未被訪問,則需要另選一個未曾被訪問過的頂點作為新的起始點,重複上述過程,直至圖中所有頂點都被訪問到為止。
換句話說,廣度優先搜尋遍歷圖的過程是以v為起點,由近至遠,依次訪問和v有路徑相通且路徑長度為1,2…的頂點。
第1步:訪問A。
第2步:依次訪問C,D,F。在訪問了A之後,接下來訪問A的鄰接點。前面已經說過,在本文實現中,頂點ABCDEFG按照順序儲存的,C在"D和F"的前面,因此,先訪問C。再訪問完C之後,再依次訪問D,F。
第3步:依次訪問B,G。在第2步訪問完C,D,F之後,再依次訪問它們的鄰接點。首先訪問C的鄰接點B,再訪問F的鄰接點G。
第4步:訪問E。在第3步訪問完B,G之後,再依次訪問它們的鄰接點。只有G有鄰接點E,因此訪問G的鄰接點E。
因此訪問順序是:A -> C -> D -> F -> B -> G -> E。
程式碼實現
我們以以下無向圖來進行測試:
儲存順序為A->B->C->D->E.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class DFSAndBFS {
//初始化頂點個數,當然也可以自己寫一個函式由使用者輸入自定義圖,這裡重點不是圖的構造
private int numVertexes = 5;
//初始化頂點
private String[] vex;
//鄰接矩陣,表明了頂點的關係
private int[][] acr;
private DFSAndBFS()
{
vex= new String[]{"A", "B", "C", "D", "E"};
acr= new int[][]{
{0, 1, 0, 1, 0},
{1, 0, 1, 0, 0},
{0, 1, 0, 0, 1},
{1, 0, 1, 0, 0},
{0, 0, 1, 0, 0}
};
}
private void printGraph(DFSAndBFS g)
{
System.out.println("圖中各頂點的關係如下圖所示(鄰接矩陣,1代表兩頂點有邊,0代表無)");
System.out.print(" ");
for(int i=0;i<g.numVertexes;i++)
{
System.out.print(g.vex[i]+" ");
}
for(int i=0;i<g.numVertexes;i++)
{
System.out.println();
System.out.print(g.vex[i]+" ");
for(int j=0;j<g.numVertexes;j++)
{
System.out.print(g.acr[i][j]+" ");
}
}
}
//深度優先遍歷,遞迴演算法
private void DFSTraverse(DFSAndBFS g)
{
//建立訪問陣列
boolean []visited=new boolean [g.numVertexes];
//初始化 訪問頂點
for(int i=0;i<visited.length;i++)
visited[i]=false;
//對未訪問頂點呼叫DFS
for(int i=0;i<visited.length;i++)
if(!visited[i])
DFS(g,i,visited);
}
//從頂點i開始進行的深度優先搜尋DFS
private void DFS(DFSAndBFS g, int i, boolean[] visited) {
visited[i]=true;
System.out.print(g.vex[i]+" ");
for(int j=0;j<g.numVertexes;j++)
{
if(g.acr[i][j]>0 && !visited[j])
DFS(g,j,visited);
}
}
//深度優先遍歷,非遞迴演算法
private void DFS(DFSAndBFS g){
boolean[] visited = new boolean[numVertexes];
Stack<Integer> stack =new Stack<Integer>();
for(int i=0;i<g.numVertexes;i++){
if(!visited[i]){
visited[i]=true;
System.out.print(g.vex[i]+" ");
stack.push(i);
}
while(!stack.isEmpty()){
int k = stack.pop();
for(int j=0;j<g.numVertexes;j++){
if(g.acr[k][j]==1&& !visited[j]){
visited[j]=true;
System.out.print(g.vex[j]+" ");
stack.push(j);
break;
}
}
}
}
}
//廣度優先搜尋
private void BFS(DFSAndBFS g)
{
int i=0;
Queue<Integer> q=new LinkedList<Integer>();
//建立訪問陣列
boolean visited[]=new boolean[numVertexes];
//初始化 訪問頂點
for(i=0;i<g.numVertexes;i++)
visited[i]=false;
//對每個頂點做迴圈
for( i=0;i<g.numVertexes;i++)
{
if(!visited[i])
{
visited[i]=true;
System.out.print(g.vex[i]+" ");
q.offer(i);
while(!q.isEmpty())
{
i=q.poll();
for(int j=0;j<g.numVertexes;j++)
{
if(g.acr[i][j]>0 && !visited[j])
{
visited[j]=true;
System.out.print(g.vex[j]+" ");
q.offer(j);
}
}
}
}
}
}
public static void main(String []args)
{
DFSAndBFS g=new DFSAndBFS();
g.printGraph(g);
System.out.println();
System.out.println("深度優先搜尋(遞迴)結果:");
g.DFSTraverse(g);
System.out.println();
System.out.println("深度優先搜尋(非遞迴)結果:");
g.DFS(g);
System.out.println();
System.out.println("廣度優先搜尋結果:");
g.BFS(g);
}
}
測試結果:
圖中各頂點的關係如下圖所示(鄰接矩陣,1代表兩頂點有邊,0代表無)
A B C D E
A 0 1 0 1 0
B 1 0 1 0 0
C 0 1 0 0 1
D 1 0 1 0 0
E 0 0 1 0 0
深度優先搜尋(遞迴)結果:
A B C E D
深度優先搜尋(非遞迴)結果:
A B C E D
廣度優先搜尋結果:
A B D C E
如果您有什麼疑問、想法或者建議都可以聯絡我哦。聯絡方式:[email protected]傳送郵件。
轉載或者引用內容請註明出處。
喜歡本文的朋友們,歡迎長按下圖二維碼並關注訂閱號程式設計師凌風,收看更多精彩的文章。