leetocode 207 課程表

解題思路:

本題可約化爲:課程安排圖是不是 有向無環圖(DAG)。即課程間規定了前置條件,但不能構成任何環路,不然課程前置條件將不成立。
思路是經過 拓撲排序 判斷此課程安排圖是不是 有向無環圖(DAG)。
拓撲排序是對 DAG 的頂點進行排序,使得對每一條有向邊 (u, v),均有 u(在排序記錄中)比 v 先出現。亦可理解爲對某點 v而言,只有當 v 的全部源點均出現了,v才能出現。
經過課程前置條件列表 prerequisites 能夠獲得課程安排圖的 鄰接矩陣 adjacency,如下兩種方法都會用到鄰接矩陣。算法

方法1:入度表(廣度優先遍歷)

算法流程:

統計課程安排圖中每一個節點的入度,生成 入度表 indegrees。
藉助一個隊列 queue,將全部入度爲 00 的節點入隊。
當 queue 非空時,依次將隊首節點出隊,在課程安排圖中刪除此節點 pre:
並非真正從鄰接表中刪除此節點 pre,而是將此節點對應全部鄰接節點 cur 的入度 -1,即 indegrees[cur] -= 1。
當入度 -1後鄰接節點 cur 的入度爲 00,說明 cur 全部的前驅節點已經被 「刪除」,此時將 cur 入隊。
在每次 pre 出隊時,執行 numCourses--;
若整個課程安排圖是有向無環圖(便可以安排),則全部節點必定都入隊並出隊過,即完成拓撲排序。換個角度說,若課程安排圖中存在環,必定有節點的入度始終不爲 0。
所以,拓撲排序出隊次數等於課程個數,返回 numCourses == 0 判斷課程是否能夠成功安排。ui

複雜度分析:

時間複雜度 O(N + M),遍歷一個圖須要訪問全部節點和全部臨邊,N 和 M分別爲節點數量和臨邊數量;
空間複雜度 O(N),爲創建鄰接矩陣所需額外空間code

class Solution {
 public boolean canFinish(int numCourses, int[][] prerequisites) {
    int[] indegrees = new int[numCourses];
    for(int[] cp : prerequisites) indegrees[cp[0]]++;
    LinkedList<Integer> queue = new LinkedList<>();
    for(int i = 0; i < numCourses; i++){
        if(indegrees[i] == 0) queue.addLast(i);
    }
    while(!queue.isEmpty()) {
        Integer pre = queue.removeFirst();
        numCourses--;
        for(int[] req : prerequisites) {
            if(req[1] != pre) continue;
            if(--indegrees[req[0]] == 0) queue.add(req[0]);
        }
    }
    return numCourses == 0;
}

}排序

方法2:深度優先遍歷

算法流程(思路是經過 DFS 判斷圖中是否有環):
藉助一個標誌列表 flags,用於判斷每一個節點 i (課程)的狀態:
未被 DFS 訪問:i == 0;
已被其餘節點啓動的DFS訪問:i == -1;
已被當前節點啓動的DFS訪問:i == 1。
對 numCourses 個節點依次執行 DFS,判斷每一個節點起步 DFS 是否存在環,若存在環直接返回 False。DFS 流程;
終止條件:
當 flag[i] == -1,說明當前訪問節點已被其餘節點啓動的 DFS 訪問,無需再重複搜索,直接返回 True。
當 flag[i] == 1,說明在本輪 DFS 搜索中節點 i 被第 2次訪問,即 課程安排圖有環,直接返回 False。
將當前訪問節點 i 對應 flag[i] 置 1,即標記其被本輪 DFS 訪問過;
遞歸訪問當前節點 i 的全部鄰接節點 j,當發現環直接返回 False;
當前節點全部鄰接節點已被遍歷,並無發現環,則將當前節點 flag 置爲 -1 並返回 True。
若整個圖 DFS 結束並未發現環,返回 True。
複雜度分析:
時間複雜度 O(N + M):遍歷一個圖須要訪問全部節點和全部臨邊,N 和 M分別爲節點數量和臨邊數量;
空間複雜度 O(N),爲創建鄰接矩陣所需額外空間。遞歸

class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
    int[][] adjacency = new int[numCourses][numCourses];
    int[] flags = new int[numCourses];
    for(int[] cp : prerequisites)
        adjacency[cp[1]][cp[0]] = 1;
    for(int i = 0; i < numCourses; i++){
        if(!dfs(adjacency, flags, i)) return false;
    }
    return true;
}
private boolean dfs(int[][] adjacency, int[] flags, int i) {
    if(flags[i] == 1) return false;
    if(flags[i] == -1) return true;
    flags[i] = 1;
    for(int j = 0; j < adjacency.length; j++) {
        if(adjacency[i][j] == 1 && !dfs(adjacency, flags, j)) return false;
    }
    flags[i] = -1;
    return true;
}

}隊列

相關文章
相關標籤/搜索