淺析"圖"的暴力美學

You can't have a better tomorrow if you are thinking about yesterday. 
若是你還在思考昨天,那麼你就沒法擁有一個更好的明天。

圖是一種比較複雜的非線性數據結構。圖分不少種,無向圖,有向圖,帶權圖,稀疏圖等等。本文主要分享了無向圖的兩種暴力搜索算法BFS(廣度優先搜索)和DFS(深度優先搜索)。全部源碼均已上傳至github: 連接git

準備

在這以前,先初始化一個無向圖,用LinkedList來當鄰接表存儲圖

private Graph(int count) {
        this.count = count;
        adj = new LinkedList[count];
        for (int i = 0; i < adj.length; i++) {
            adj[i] = new LinkedList<>();
        }
    }
複製代碼

這裏爲了插入方便,一條邊直接存兩次

private void add(int oSide, int rSide) {
        adj[oSide].add(rSide);
        adj[rSide].add(oSide);
    }
複製代碼

構造一個無向圖

private void createGraph() {
        //  0 -- 1 -- 2
        //  |    |    |
        //  3 -- 4 -- 5
        //  |    |    |
        //  6 -- 7 -- 8
        add(0,1);//add(1,0);
        add(0,3);//add(3,0);

        add(1,2);//add(2,1);
        add(1,4);// add(4,1);

        add(2,5);//add(5,2);

        add(3,4);//add(4,3);
        add(3,6);//add(6,3);

        add(4,5);//add(5,4);
        add(4,7);// add(7,4);

        add(5,8);//add(8,5);

        add(6,7);//add(7,6);

        add(7,8);//add(8,7);
    }
複製代碼

準備完畢github

BFS(廣度優先搜索)

分析

廣度優先搜索,從字面意思理解,它就是一種「地毯式」的搜索策略,先查找離起始頂點最近的,而後是次近的,依次往外搜索,層層遞進。算法

注意

在這裏三個重要的核心輔助變量 visited、queue、prev。bash

  • visited 記錄已經被訪問的頂點,避免頂點被重複訪問
  • queue 用來存儲已經被訪問、但相連的頂點尚未被訪問的頂點的這樣的一個隊列。
  • prev 記錄搜索路徑,它是反向存儲,便於後續正向打印輸出圖的路徑。

這裏看一下它的打印方式就理解prev的存儲機制了。數據結構

private void print(int[] prev, int oSide, int rSide) {
        if (prev[rSide] != -1 && oSide != rSide) {
            print(prev, oSide, prev[rSide]);
        }
        System.out.print(rSide + " ");
    }
複製代碼

解析

首先先將起點oSide加入隊列queue,而後在該while循環裏,遍歷該節點的鄰接表,再經過visited進行判斷,看是否訪問,一直到遍歷出的value == rSide 說明已找到,不然讓visited 記錄該頂點已訪問,同時入隊,就這樣經過不停地入隊出隊。ide

ps:遍歷出的圖的路徑也是它的最短路徑。測試

private void bfs(int oSide, int rSide) {
        if (oSide == rSide) return;

        boolean[] visited = new boolean[count];
        visited[oSide] = true;
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(oSide);
        int[] prev = new int[count];
        for (int i = 0; i < count; i++) {
            prev[i] = -1;
        }
        while (!queue.isEmpty()) {
            int index = queue.poll();
            for (int j = 0; j < adj[index].size(); j++) {
                int value = adj[index].get(j);
                if (!visited[value]) {
                    prev[value] = index;
                    if (value == rSide) {
                        print(prev, oSide, rSide);
                    }
                    visited[value] = true;
                    queue.offer(value);
                }
            }
        }
    }複製代碼

DFS(深度優先搜索)

分析

深刻優先搜索,有點走迷宮的意思,隨機選擇路徑,當發現走不通的時候,退回來從新來過,直到找到終點。經過測試結果也能夠看出它把全部的路徑都訪問了一遍,蛇皮走位。它用到了一種十分著名的思想--回溯思想。就像是'後悔藥'。ui

解析

它和bfs相同的是均用到了prev、visited。不一樣的是,它聲明瞭一個全局的變量found,起到一個跳出遞歸的做用。this

它的關鍵在於recursiveDfs這個遞歸方法。默認起點被訪問,先聲明一個出口,當知足查找的頂點等於終點的時候跳出。遍歷當前頂點的鄰接表,逐個判斷頂點是否已訪問,記錄下來,而後遞歸。spa

ps:遍歷出的圖的路徑也是它的最長路徑。

private boolean found = false;

   private void dfs(int oSide, int rSide) {
        boolean[] visited = new boolean[count];
        int[] prev = new int[count];
        for (int i = 0; i < count; i++) {
            prev[i] = -1;
        }
        recursiveDfs(visited, prev, oSide, rSide);
        print(prev, oSide, rSide);
    }

    private void recursiveDfs(boolean[] visited, int[] prev, int oSide, int rSide) {
        if (found) return;
        visited[oSide] = true;
        if (oSide == rSide) {
            found = true;
            return;
        }
        for (int i = 0; i < adj[oSide].size(); i++) {
            int value = adj[oSide].get(i);
            if (!visited[value]) {
                prev[value] = oSide;
                recursiveDfs(visited, prev, value, rSide);
            }
        }
    }
複製代碼

測試代碼

public static void main(String[] args) {
    int count = 9;
    Graph graph = new Graph(count);
    //  0 -- 1 -- 2
    //  |    |    |
    //  3 -- 4 -- 5
    //  |    |    |
    //  6 -- 7 -- 8
    graph.createGraph();
    System.out.println("BFS(廣度優先搜索)");
    graph.bfs(0,8);
    System.out.println();
    System.out.println("DFS(深度優先搜索)");
    graph.dfs(0,8);
}複製代碼

測試結果


end


您的點贊和關注是對我最大的支持,謝謝!
相關文章
相關標籤/搜索