圖系列(二)圖的遍歷與拓撲排序

圖系列(二)圖的遍歷與拓撲排序

        昂,還是希望多一點人看我的博客!好了,接下來是重中之重圖的遍歷。圖遍歷是很多其他算法的基礎,比如Dijkstra算法。

圖的遍歷

廣度優先遍歷

       廣度優先遍歷的關鍵是需要藉助一個隊列。代碼如下:

class Solution {
    List<Integer>[] adjs;
    public void bfs(int n, int[][] edges) {
        // 初始化鄰接鏈表
        // ...
        Queue<Integer> q = new LinkedList<>();
        q.add(0); // 假設起點從0開始, 通過廣度優先遍歷可以遍歷所有元素。
        
        while(!q.isEmpty()) {
            int i = q.poll();
            for (Integer adj : adjs[i]) {
                q.add(adj);
            }
        }
    }
}

        這裏做了好幾個假設: 1. 起點從0開始;2. 從0可以遍歷到所有元素。相應的,爲了解除這兩個假設,首先,我們可以從特定的點開始,例如入度爲0的點;其次,使用一個數組,存儲每個點的訪問狀態,如果元素還沒有訪問,下一輪繼續訪問。舉個栗子:

class Solution {
    List<Integer>[] adjs;
    boolean[] alreadyVisit;
    public void bfs(int n, int[][] edges) {
        // 初始化鄰接鏈表
        // ...
        alreadyVisit = new boolean[n];

        Queue<Integer> q = new LinkedList<>();
        for(int i = 0 ; i < n;i++) {
            if (!alreadyVisit[i]) {
                q.add(i);
                while (!q.isEmpty()) {
                    int head = q.poll();
                    alreadyVisit[head] = true;
                    for (Integer adj : adjs[head]) {
                        if (!alreadyVisit[adj]) {
                            q.add(adj);
                        }
                    }
                }
            }
        }
    }
}

參見leetcode題目: 課程表

深度優先遍歷

        深度優先遍歷,主要利用遞歸。當然,我們不能夠一直避開環的問題。這裏介紹《算法與數據結構》中典型的處理方式—— 塗色。沒有訪問過的元素塗成白色,已經訪問過的元素塗成黑色,正在訪問,在同一條遞歸調用中的元素塗成灰色。代碼如下:

class Solution {
    private List<Integer>[] adjs;
    private Color[] colors;
    public boolean containsLoop(int n, int[][] edges) {
        // 初始化
        adjs = new List[n];
        colors = new Color[n];
        for(int i = 0; i < n; i++) {
            adjs[i] = new LinkedList<>();
            colors[i] = Color.WHITE;
        }
        for(int i = 0 ; i<edges.length;i++) {
            int[] edge = edges[i];
            int from = edge[0];
            int to = edge[1];
            adjs[from].add(to);
        }


        for (int i = 0; i < n;i++) {
            if (colors[i] == Color.WHITE) {
                if(dfsVisit(i)) return true;
            }
        }
        return false;
    }

    // 判斷是否存在環,存在立即返回true
    // 不存在返回false。
    private boolean dfsVisit(int i) {
        colors[i] = Color.GRAY;
        for (Integer adj: adjs[i]) {
            if (colors[adj] == Color.GRAY) return true;
            if (colors[adj] == Color.WHITE) {
                if (dfsVisit(adj)) return true;
            }
        }
        colors[i] = Color.BLACK;
        return false;
    }
    private enum Color {
        WHITE, GRAY, BLACK
    }
}

       其實,蠻簡單的,多些幾遍就會了。

拓撲排序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章