圖的深度優(yōu)先遍歷和廣度優(yōu)先遍歷

圖的遍歷

  • 圖的遍歷是和樹的遍歷類似,我們希望從圖中某一頂點出發(fā)訪遍圖中其余頂點,且使每一個頂點僅被訪問一次,這一過程就叫做圖的遍歷(Traversing Graph)。
  • 重復訪問頂點就不叫做遍歷了。
  • 關于圖的基本概念,理論知識不想說了。太繁瑣~
  • 直接上圖,這個應該都能看懂。
圖的深度優(yōu)先遍歷.png
  • 左圖是一個,右圖是根據(jù)圖生成的矩陣
[v0][v1]代表v0頂點到v1頂點的路徑權重為10
可以看見右圖,權重為0的都是頂點自己到自己,這根本就沒有意義。
而無限大符號表示到達不了,比如[v0][v2]。可以看左圖,v0只能到達v1和v5,到不了v2。

第一幅圖的矩陣可以看到有9個頂點,組成二維數(shù)組

我們可以先生成這個矩陣:

public class Graph {
    
    private int vertexSize; // 頂點數(shù)量
    private int[] vertexs; // 頂點數(shù)組
    private int[][] matrix; // 包含所有頂點的數(shù)組
    // 路徑權重
    // 0意味著頂點自己到自己,無意義
    // MAX_WEIGHT也意味著到目的頂點不可達
    private static final int MAX_WEIGHT = 1000;
    
    public Graph(int vertextSize) {
        this.vertexSize = vertextSize;
        matrix = new int[vertextSize][vertextSize];
        vertexs = new int[vertextSize];
        for (int i = 0; i < vertextSize; i++) {
            vertexs[i] = i;
        }
    }
    
    public static void main(String[] args) {
        Graph graph = new Graph(9);

        // 頂點的矩陣設置
        int[] a1 = new int[] { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] a2 = new int[] { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
        int[] a3 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
        int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, 24, 16, 21 };
        //int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
        int[] a5 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
        int[] a6 = new int[] { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
        int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
        //int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
        int[] a8 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
        int[] a9 = new int[] { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };

        graph.matrix[0] = a1;
        graph.matrix[1] = a2;
        graph.matrix[2] = a3;
        graph.matrix[3] = a4;
        graph.matrix[4] = a5;
        graph.matrix[5] = a6;
        graph.matrix[6] = a7;
        graph.matrix[7] = a8;
        graph.matrix[8] = a9;
    }
}
  • 可以看到我們用一個matrix二維數(shù)組存放所有頂點
  • 0表示自己到自己,MAX_WEIGHT表示無限大
  • 然后就是一些實例化設置矩陣
  • 有了這些,我們才能進行遍歷

深度優(yōu)先遍歷

  • 深度優(yōu)先遍歷(Depth First Search),也有稱為深度優(yōu)先搜索,簡稱為DFS。
深度優(yōu)先遍歷.png
  • 遍歷規(guī)則:不斷的沿著頂點的深度方向遍歷。頂點的深度方向是指它的鄰接點方向。
  • 它從圖中某個頂點v出發(fā),訪問此頂點,然后從頂點的未被訪問的鄰接點出發(fā)深度優(yōu)先遍歷圖,直至圖中所有和v有路徑相通的頂點都被訪問到。
  • 簡單說,就是頂點將第一個鄰接點當作左孩子,其它鄰接點都當做右孩子。最后排成一棵樹。
  • 深度優(yōu)先遍歷是指先遍歷到最深層次然后再探索鄰接點,接著又遍歷最深層次。二叉樹的先序遍歷就是一種深度優(yōu)先遍歷。

======================================

  • 思想與步驟
  • 看上圖右圖,我們先訪問A,然后訪問A的第一個鄰接點B。接著訪問B的第一個鄰接點C。。。。最后訪問到F,F(xiàn)想訪問第一個鄰接點A。但是A已經(jīng)訪問過了,只能訪問F的下一個鄰接點G。
  • 這就是深度優(yōu)先遍歷的訪問順序
  • 在代碼中,依照分析。獲取某頂點的第一個鄰接點和下一個臨界點是經(jīng)常使用的方法。訪問過程中,我們要判斷該頂點是否已訪問過,這個也需要輔助變量。
private boolean[] isVisited = new boolean[vertextSize];

/**
 * 獲取指定頂點的第一個鄰接點
 * 
 * @param index
 *          指定鄰接點
 * @return
 */
private int getFirstNeighbor(int index) {
    for (int i = 0; i < vertexSize; i++) {
        if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
            return i;
        }
    }
    return -1;
}

/**
 * 獲取指定頂點的下一個鄰接點
 * 
 * @param v
 *          指定的頂點
 * @param index
 *          從哪個鄰接點開始
 * @return
 */
private int getNextNeighbor(int v, int index) {
    for (int i = index+1; i < vertexSize; i++) {
        if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
            return i;
        }
    }
    return -1;
}

核心代碼很簡單,經(jīng)上述分析過后:

/**
 * 圖的深度優(yōu)先遍歷算法
 */
private void depthFirstSearch(int i) {
    isVisited[i] = true;
    int w = getFirstNeighbor(i);
    while (w != -1) {
        if (!isVisited[w]) {
            // 需要遍歷該頂點
            System.out.println("訪問到了:" + w + "頂點");
            depthFirstSearch(w); // 進行深度遍歷
        }
        w = getNextNeighbor(i, w); // 第一個相對于w的鄰接點
    }
}
  • 0進去,表示v0
  • 設置v0已訪問過,獲取v0第一個鄰接點w != -1說明有這個鄰接點,然后對這個臨界點進行判斷。
    • 已訪問,那就找下一個臨界點
    • 未訪問,進行訪問,然后對該鄰接點進行深度優(yōu)先遍歷
  • 算法還是很簡單的!

廣度優(yōu)先遍歷

  • 思想(感悟):
  • 廣度優(yōu)先遍歷表示把每一層都遍歷完才能遍歷下一層
  • 我們來思考:假設v0有3個鄰接點,v1 v2 v3
    • 我們訪問v0后,然后訪問v1 v2 v3。完畢后我們要從v1開始遍歷它的鄰接點,接著從v2開始遍歷它的鄰接點,最后是從v3開始遍歷它的鄰接點。
    • 也就是說,3個鄰接點訪問完后。我們要回過頭逐個遍歷它們的鄰接點。這一點我覺得要用個容器把它們順序存儲下來。然后每次從容器首部取出一個頂點開始遍歷。這里我想到LinkedList,因為它適合增刪。而且這里不需要遍歷集合。
  • 整體步驟:
    • 我們可以把第一個頂點放進集合,然后while(!queue.isEmpty())while(queue.size() > 0)都行。開始循環(huán)。
    • 然后取出并刪除集合中第一個頂點元素的第一個鄰接點。對這個頂點進行訪問,
      • 如果該頂點未訪問過,就訪問!然后將該頂點放入集合。
      • 如果該頂點已訪問過,就找該頂點的下一個鄰接點。

核心代碼:

/**
 * 圖的廣度優(yōu)先遍歷算法
 */
private void boardFirstSearch(int i) {
    LinkedList<Integer> queue = new LinkedList<>(); 
    System.out.println("訪問到了:" + i + "頂點");
    isVisited[i] = true;
    queue.add(i);
    
    while (queue.size() > 0) {
        int w = queue.removeFirst().intValue();
        int n = getFirstNeighbor(w);
        while (n != -1) {
            if (!isVisited[n]) {
                System.out.println("訪問到了:" + n + "頂點");
                isVisited[n] = true;
                queue.add(n);
            }
            n = getNextNeighbor(w, n);
        }
    }
}

完整代碼

復制即可運行

import java.util.LinkedList;

public class Graph {
    
    private int vertexSize; // 頂點數(shù)量
    private int[] vertexs; // 頂點數(shù)組
    private int[][] matrix; // 包含所有頂點的數(shù)組
    // 路徑權重
    // 0意味著頂點自己到自己,無意義
    // MAX_WEIGHT也意味著到目的頂點不可達
    private static final int MAX_WEIGHT = 1000;
    private boolean[] isVisited; // 某頂點是否被訪問過
    
    public Graph(int vertextSize) {
        this.vertexSize = vertextSize;
        matrix = new int[vertextSize][vertextSize];
        vertexs = new int[vertextSize];
        for (int i = 0; i < vertextSize; i++) {
            vertexs[i] = i;
        }
        isVisited = new boolean[vertextSize];
    }
    
    /**
     * 獲取指定頂點的第一個鄰接點
     * 
     * @param index
     *          指定鄰接點
     * @return
     */
    private int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexSize; i++) {
            if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * 獲取指定頂點的下一個鄰接點
     * 
     * @param v
     *          指定的頂點
     * @param index
     *          從哪個鄰接點開始
     * @return
     */
    private int getNextNeighbor(int v, int index) {
        for (int i = index+1; i < vertexSize; i++) {
            if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * 圖的深度優(yōu)先遍歷算法
     */
    private void depthFirstSearch(int i) {
        isVisited[i] = true;
        int w = getFirstNeighbor(i);
        while (w != -1) {
            if (!isVisited[w]) {
                // 需要遍歷該頂點
                System.out.println("訪問到了:" + w + "頂點");
                depthFirstSearch(w); // 進行深度遍歷
            }
            w = getNextNeighbor(i, w); // 第一個相對于w的鄰接點
        }
    }
    
    /**
     * 圖的廣度優(yōu)先遍歷算法
     */
    private void boardFirstSearch(int i) {
        LinkedList<Integer> queue = new LinkedList<>(); 
        System.out.println("訪問到了:" + i + "頂點");
        isVisited[i] = true;
        queue.add(i);
        
        while (queue.size() > 0) {
            int w = queue.removeFirst().intValue();
            int n = getFirstNeighbor(w);
            while (n != -1) {
                if (!isVisited[n]) {
                    System.out.println("訪問到了:" + n + "頂點");
                    isVisited[n] = true;
                    queue.add(n);
                }
                n = getNextNeighbor(w, n);
            }
        }
    }

    public static void main(String[] args) {
        Graph graph = new Graph(9);

        // 頂點的矩陣設置
        int[] a1 = new int[] { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] a2 = new int[] { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
        int[] a3 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
        int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, 24, 16, 21 };
        //int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
        int[] a5 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
        int[] a6 = new int[] { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
        int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, 24, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
        //int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
        int[] a8 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
        int[] a9 = new int[] { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };

        graph.matrix[0] = a1;
        graph.matrix[1] = a2;
        graph.matrix[2] = a3;
        graph.matrix[3] = a4;
        graph.matrix[4] = a5;
        graph.matrix[5] = a6; 
        graph.matrix[6] = a7;
        graph.matrix[7] = a8;
        graph.matrix[8] = a9;
        
        graph.depthFirstSearch(0);
        //graph.boardFirstSearch(0);
    }

}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容