1. 程式人生 > >圖的BFS和DFS原理及例項分析(java)

圖的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的特徵以及它們關於非啟發式圖搜尋演算法的知識後續將進行補充。如有何問題歡迎指正,謝謝!