1. 程式人生 > 其它 >無向圖的鄰接矩陣建立及DFS和BFS遍歷

無向圖的鄰接矩陣建立及DFS和BFS遍歷

一.圖的定義

定義:圖(Graph)是由頂點的有窮非空集合和頂點之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。

圖的分類:

圖是按照無方向和有方向分為無向圖和有向圖。

左圖為無向圖是由頂點和邊構成,右圖為有向圖是由頂點和弧(有向邊構成)。弧有弧頭和弧尾區別

二、圖的儲存結構

1、鄰接矩陣

鄰接矩陣用兩個陣列儲存資料。一個一維陣列儲存圖中頂點資訊,一個二維陣列儲存圖中邊或弧的資訊。

2、鄰接表

鄰接表:陣列和連結串列相結合的儲存方法為鄰接表。

三.鄰接矩陣的實現

public class Graph {
    private ArrayList<String> vertexList;//儲存頂點集合
    private int[][] edges; //儲存圖對應的鄰接矩陣
    private int numOfEdge; //表示邊的數目
    private boolean[] isVisited;

    public static void main(String[] args) {
        int n = 5;
        Graph graph = new Graph(n);
        String[] vertexValue = { "A", "B", "C", "D", "E" };
        // 建立各頂點所對應的值
        for (String vertex : vertexValue) {
            graph.insertVertex(vertex);
        }
        // 構造邊的關係
        graph.insertEdges(0, 0, 1);
        graph.insertEdges(0, 2, 1);
        graph.insertEdges(1, 2, 1);
        graph.insertEdges(1, 3, 1);
        graph.insertEdges(1, 4, 1);
        graph.showGraph();

        //深度優先遍歷
        System.out.println("深度優先遍歷");
        graph.dfs();
        System.out.println();
        //廣度優先遍歷
        System.out.println("廣度優先遍歷");
        graph.bfs();
    }

    /**
     *
     * @param //頂點個數
     */
    public Graph(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
    }

    /**
     *
     * @Title: insertVertex
     * @Description: 向頂點中加入該頂點的資料
     * @param @param vertex 要插入的資料
     * @return void 返回型別
     */
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }

    /**
     *
     * @Title: insertEdges
     * @Description: 將鄰接矩陣各個結點之間的關係建立起來,1代表相連,0代表不相連
     * @param @param v1 代表下標為v1的頂點
     * @param @param v2 代表下標為v2的頂點
     * @param @param weight 權值,不是0就是1
     * @return void 返回型別
     */
    public void insertEdges(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdge++;
    }

    // 返回結點數
    public int getNumOfVertex() {
        return vertexList.size();
    }

    // 返回邊數
    public int getNumOfEdges() {
        return numOfEdge;
    }

    // 返回i對應的資料
    public String getValueByyIndex(int i) {
        return vertexList.get(i);
    }

    // 返回v1和v2的權值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    // 顯示圖對應的矩陣
    public void showGraph() {
        for (int[] link : edges) {
            System.out.println(Arrays.toString(link));
        }
    }
}

四.圖的深度優先遍歷

深度優先遍歷(Depth-First-Search),從初始訪問結點出發,初始訪問結點可能有多個鄰接結點,深度優先遍歷的策略就是首先訪問,第一個鄰接結點,然後再以這個被訪問的鄰接結點作為初始結點,訪問它的第一個鄰接結點, 可以這樣理解每次都在訪問完當前結點後首先訪問當前結點的第一個鄰接結點。

深度優先搜尋是一個遞迴的過程。

1. 深度優先遍歷演算法步驟

1、訪問初始頂點x,訪問後需要標記x已經訪問過,不能再次重讀訪問

2、查詢頂點x的第一個鄰接點y

3、如果y存在,則繼續執行下面步驟,如果y不存在,則回到第1步,將從x的下一個頂點繼續

4、如果y未被訪問過,對y進行深度優先遍歷,則是把y當做另一個x,然後執行123步

5、查詢頂點x的y鄰接點的下一個鄰接點,轉到步驟3

 //得到第一個臨結節點的下標w
    //如果存在就返回對應的下標
    public int getFirstNeighbor(int index){
        for (int i = 0; i < vertexList.size(); i++) {
            if (edges[index][i] > 0){
                return i;
            }
        }
        return -1;
    }

    //根據前一個鄰接節點的下標來獲取下一個鄰接節點
    public int getNextNeighbor(int v1,int v2){
        for (int i = v2+1; i < vertexList.size(); i++) {
            if (edges[v1][i] > 0){
                return i;
            }
        }
        return -1;
    }

    //對dfs過載,遍歷所有的節點
    public void dfs(){
        isVisited = new boolean[5];
        //遍歷所有節點進行dfs[回溯]
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }
    //深度優先遍歷
    public void dfs(boolean[] isVisited,int i){
    //首先訪問該節點
        System.out.print(getValueByyIndex(i)+"->");
        //將該節點設定為已訪問
        isVisited[i] = true;
        //查詢節點i的第一個鄰接節點w
        int w =getFirstNeighbor(i);
        while (w!=-1){
            if (!isVisited[w]){
                dfs(isVisited,w);
            }
            //如果w節點被訪問過
            w = getNextNeighbor(i,w);
        }
    }

五.廣度優先遍歷

類似於一個 分層搜尋的過程,廣度優先遍歷(Breadth-First-Search)需要使用一個佇列以保持訪問過的結點的順序,以便按這個順序來訪問這些結點的鄰接結點

1 廣度優先遍歷演算法分析

1)訪問初始結點 v 並標記結點 v 為已訪問。

  1. 結點 v 入佇列

  2. 當佇列非空時,繼續執行,否則演算法結束。

  3. 出佇列,取得隊頭結點 u。

  4. 查詢結點 u 的第一個鄰接結點 w。

  5. 若結點 u 的鄰接結點 w 不存在,則轉到步驟 3;否則迴圈執行以下三個步驟

6.1若結點 w 尚未被訪問,則訪問結點 w 並標記為已訪問。

6.2 結點 w 入佇列

6.3 查詢結點 u 的繼 w 鄰接結點後的下一個鄰接結點 w,轉到步驟 6

   //遍歷所有節點
    public void bfs() {
        isVisited = new boolean[5];
        //遍歷所有節點進行bfs
        for (int i = 0; i < getNumOfVertex(); i++) {
            if (!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
    //進行廣度優先遍歷
    private void bfs(boolean[] isVisited, int i) {
        int u; //表示佇列頭節點對應下標
        int w; //鄰接節點
        //佇列,記錄節點訪問的順序
        LinkedList list = new LinkedList();
        //訪問節點 輸出節點資訊
        System.out.print(getValueByyIndex(i) + "->");
        //標記為已訪問
        isVisited[i] = true;
        //將節點加入佇列
        list.addLast(i);
        while (!list.isEmpty()) {
            //取出佇列頭結點下標
            u = (Integer) list.removeFirst();
            //得到第一個臨界點的下標
            w = getFirstNeighbor(u);
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByyIndex(w) + "->");
                    //標記為已訪問
                    isVisited[w] = true;
                    //入隊
                    list.addLast(w);
                }
                //以u找w後面的下一個鄰接點
                w = getNextNeighbor(u, w);
            }
        }
    }