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