圖的BFS和DFS原理及例項分析(java)
BFS和DFS是圖的兩種遍歷方式,是最簡單的圖搜尋演算法。
本文將給出給出BFS和DFS的以下幾種實現方式:
1、使用佇列Queue實現圖的BFS遍歷
2、遞迴實現圖的DFS遍歷
3、使用棧Stack迭代實現圖的DFS遍歷
一、BFS(廣度優先搜尋演算法)
BFS演算法之所以叫做廣度優先搜尋,是因為它始終將已發現的頂點和未發現的之間的邊界,沿其廣度方向向外擴充套件。亦即,演算法首先會發現和s距離為k的所有頂點,然後才會發現和s距離為k+1的其他頂點。
同深度優先搜尋相反,BFS寬度優先搜尋每次選擇深度最淺的節點優先擴充套件。並且當問題有解時,寬度優先演算法一定能夠找到解,並且在單位耗散時間的情況下,可以保證找到最優解。
二、DFS(深度優先搜尋演算法)
DFS演算法利用遞迴方式實現,和BFS不同的是BFS搜尋產生的始終是一棵樹,而DFS產生的可能會使一個森林。
對於深度優先搜尋演算法的思想。在一般情況下,當問題有解時,深度優先搜尋不但不能夠保證找到最優解,也不能保證找到解。如果問題狀態空間有限,則可以保證找到解;但是當問題的狀態空間無限時,則可能陷入“深淵”而找不到解。為此我們可以利用回溯演算法中的思想,可以加上對搜尋的深度限制。從而實現對於搜尋深度的限制。當然深度限制設定必須合理,深度過深則影響搜尋的效率,深度過淺時,則可能影響找到問題的解。
使用棧實現DFS思路關鍵點:
1、首先明確整個DFS主要便是對於棧進行操作,就是在頂點壓棧和彈棧過程中我們需要進行的操作;
2、利用DFS的思想,深度遍歷節點。直到棧內元素為空位置;
3、何時進行壓棧:對於棧頂頂點,看其鄰接頂點中是夠存在未被遍歷過得白色頂點,若有則對將其壓棧,然後再對棧頂元素進行操作;
4、如果棧頂頂點的所有鄰接頂點都是被遍歷過的灰色頂點,則將棧頂元素彈棧,然後再對現在的棧頂元素進行操作;
5、演算法結束時,所有元素均被遍歷過即為灰色,並且棧已經為空。
三、BFS和DFS實現(java)
1、Vertex物件類
public class Vertex1 {
String verName;
String color;
int discoverTime;
int finishTime;
Vertex1 nextNode;
}
2、Graph物件類
public class Graph1 {
Vertex1[] vertexArray=new Vertex1[100];
int verNum=0;
int edgeNum=0;
}
3、核心實現類
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Stack;
/**
* <pre>無權有向圖和無向圖的構建以及實現了圖的BFS遍歷和DFS遍歷:
* 1>.使用佇列Queue實現圖的BFS遍歷;
* 2>.遞迴實現圖的DFS遍歷;
* 3>.使用棧Stack迭代實現圖的DFS遍歷。</pre>
* @author King
*/
public class CreateGraph1 {
int time=0;
Stack<Vertex1> stackVertex=new Stack<Vertex1>();
public static void main(String[] args) {
Graph1 graph=new Graph1();
CreateGraph1 createGraph=new CreateGraph1();
createGraph.initialGraph(graph);
createGraph.outputGraph(graph);
// System.out.println("DFS搜尋路徑為(遞迴實現):");
// createGraph.DFS(graph);
System.out.println("DFS搜尋路徑為(棧實現):");
createGraph.stackMain(graph);
// System.out.println("BFS搜尋路徑為:");
// createGraph.BFS(graph);
}
/**
* 根據使用者輸入的string型別的頂點返回該頂點
* @param graph 圖
* @param str 輸入資料
* @return返回一個頂點
*/
public Vertex1 getVertex(Graph1 graph,String str){
for(int i=0;i<graph.verNum;i++){
if(graph.vertexArray[i].verName.equals(str)){
return graph.vertexArray[i];
}
}
return null;
}
/**
* 根據使用者輸入的資料初始化一個圖,以鄰接表的形式構建!
* @param graph 生成的圖
*/
public void initialGraph(Graph1 graph){
@SuppressWarnings("resource")
Scanner scan=new Scanner(System.in);
System.out.println("請輸入頂點數和邊數:");
graph.verNum=scan.nextInt();
graph.edgeNum=scan.nextInt();
System.out.println("請依次輸入定點名稱:");
for(int i=0;i<graph.verNum;i++){
Vertex1 vertex=new Vertex1();
String name=scan.next();
vertex.verName=name;
vertex.color="white";
vertex.discoverTime=0;
vertex.finishTime=0;
vertex.nextNode=null;
graph.vertexArray[i]=vertex;
}
System.out.println("請依次輸入圖的便邊:");
for(int i=0;i<graph.edgeNum;i++){
String preV=scan.next();
String folV=scan.next();
Vertex1 v1=getVertex(graph,preV);
if(v1==null)
System.out.println("輸入邊存在圖中沒有的頂點!");
Vertex1 v2=new Vertex1();
v2.verName=folV;
v2.nextNode=v1.nextNode;
v1.nextNode=v2;
// 緊接著下面註釋的程式碼加上便是構建無向圖的,不加則是構建有向圖的!
// Vertex1 reV2=getVertex(graph,folV);
// if(reV2==null)
// System.out.println("輸入邊存在圖中沒有的頂點!");
// Vertex1 reV1=new Vertex1();
// reV1.verName=preV;
// reV1.nextNode=reV2.nextNode;
// reV2.nextNode=reV1;
}
}
/**
* 輸入圖的鄰接表
* @param graph 待輸出的圖
*/
public void outputGraph(Graph1 graph){
System.out.println("輸出圖的鄰接連結串列為:");
for(int i=0;i<graph.verNum;i++){
Vertex1 vertex=graph.vertexArray[i];
System.out.print(vertex.verName);
Vertex1 current=vertex.nextNode;
while(current!=null){
System.out.print("-->"+current.verName);
current=current.nextNode;
}
System.out.println();
}
}
/**
* DFS遍歷輔助函式,標記顏色是輔助,即根據頂點返回其下標
* @param vertex 頂點
* @param graph 圖
* @return返回下標
*/
public int index(Vertex1 vertex,Graph1 graph){
for(int i=0;i<graph.verNum;i++){
if(vertex.verName.equals(graph.vertexArray[i].verName))
return i;
}
return -1;
}
/**
* DFS深度優先遍歷初始化
* @param graph 圖
*/
public void DFS(Graph1 graph){
for(int i=0;i<graph.verNum;i++){
if(graph.vertexArray[i].color.equals("white")){
DfsVisit(graph.vertexArray[i],graph);
System.out.println();
}
}
}
/**
* DFS遞迴函式
* @param vertex 頂點
* @param graph 圖
*/
public void DfsVisit(Vertex1 vertex,Graph1 graph){
vertex.color="gray";
time=time+1;
vertex.discoverTime=time;
System.out.print(vertex.verName+"-->");
Vertex1 current=vertex.nextNode;
while(current!=null){
Vertex1 currentNow=getVertex(graph, current.verName);
if(currentNow.color.equals("white"))
DfsVisit(currentNow,graph);
current=current.nextNode;
}
vertex.color="black";
time=time+1;
vertex.finishTime=time;
}
/**
* 尋找一個節點的鄰接點中是否還有白色節點
* @param vertex 頂點
* @param graph 圖
* @return 返回白色節點或是null
*/
public Vertex1 getAdj(Graph1 graph,Vertex1 vertex){
Vertex1 ver=getVertex(graph, vertex.verName);
Vertex1 current=ver.nextNode;
if(current==null)
return null;
else{
Vertex1 cur=getVertex(graph, current.verName);
while(current!=null && cur.color.equals("gray")){
current=current.nextNode;
}
if(cur.color.equals("white")){
Vertex1 currentNow=getVertex(graph, current.verName);
return currentNow;
}else{
return null;
}
}
}
/**
* 通過棧實現dfs遍歷
* @param graph 圖
* @param vertex 頂點
*/
public void stackOperator(Graph1 graph,Vertex1 vertex){
vertex.color="gray";
stackVertex.push(vertex);
System.out.print(vertex.verName+"-->");
while(!stackVertex.isEmpty()){
Vertex1 ver=stackVertex.peek();
Vertex1 current=getAdj(graph,ver);
if(current!=null){
stackVertex.push(current);
current.color="gray";
System.out.print(current.verName+"-->");
}else{
stackVertex.pop();
}
}
}
/**
* DFS遍歷主函式
* @param graph
*/
public void stackMain(Graph1 graph){
for(int i=0;i<graph.verNum;i++){
if(graph.vertexArray[i].color.equals("white")){
stackOperator(graph,graph.vertexArray[i]);
System.out.println();
}
}
}
/**
* BFS廣度優先搜尋實現
* @param graph 圖
*/
public void BFS(Graph1 graph){
Vertex1 current=graph.vertexArray[0];
current.color="gray";
time=time+1;
current.discoverTime=time;
Queue<Vertex1> queue=new LinkedList<Vertex1>();
queue.offer(current);
while(queue.peek()!=null){
Vertex1 ver=queue.poll();
time=time+1;
ver.finishTime=time;
System.out.print(ver.verName+"-->");
Vertex1 cur=ver.nextNode;
while(cur!=null){
Vertex1 curNow=getVertex(graph, cur.verName);
if(curNow.color.equals("white")){
curNow.color="gray";
time=time+1;
curNow.discoverTime=time;
queue.offer(curNow);
}
cur=cur.nextNode;
}
}
System.out.println("null");
}
}
/*
DFS測試圖的邊:
v1 v2
v1 v3
v2 v3
v3 v4
v4 v2
v5 v4
v5 v6
BFS測試圖的邊(10):
v1 v2
v1 v4
v2 v3
v4 v5
v4 v8
v5 v6
v5 v7
v5 v8
v6 v7
v7 v8
*/
BFS測試所用的無向圖為:
BFS測試輸入資料:
測試結果為:
DFS測試所用的有向圖為:
DFS測試輸入資料
:
DFS測試結果:
關於BFS和DFS的特徵以及它們關於非啟發式圖搜尋演算法的知識後續將進行補充。如有何問題歡迎指正,謝謝!