1. 程式人生 > >廣度優先搜尋與深度優先搜尋

廣度優先搜尋與深度優先搜尋

廣度優先搜尋(寬度優先搜尋,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]傳送郵件。

轉載或者引用內容請註明出處。

喜歡本文的朋友們,歡迎長按下圖二維碼並關注訂閱號程式設計師凌風,收看更多精彩的文章。
在這裡插入圖片描述