【力扣】207.課程表
題目描述
你這個學期必須選修 numCourses 門課程,記為0到numCourses - 1 。
在選修某些課程之前需要一些先修課程。 先修課程按陣列prerequisites 給出,其中prerequisites[i] = [




例如,先修課程對[0, 1] 表示:想要學習課程 0 ,你需要先完成課程 1 。
請你判斷是否可能完成所有課程的學習?如果可以,返回 true ;否則,返回 false 。
來源:力扣(LeetCode)
連結:https://leetcode-cn.com/problems/course-schedule
著作權歸領釦網路所有。商業轉載請聯絡官方授權,非商業轉載請註明出處。
示例 1:
輸入:numCourses = 2, prerequisites = [[1,0]]
輸出:true
解釋:總共有 2 門課程。學習課程 1 之前,你需要完成課程 0 。這是可能的。
示例 2:
輸入:numCourses = 2, prerequisites = [[1,0],[0,1]]
輸出:false
解釋:總共有 2 門課程。學習課程 1 之前,你需要先完成課程 0 ;並且學習課程 0 之前,你還應先完成課程 1 。這是不可能的。
分析
關鍵的資訊在“要學習課程 

解題
主要分為2個步驟1.根據prerequisites構建鄰接表 2.查詢環
構建鄰接表時,要注意
圖中的節點個數為numCourses,即課程數。
prerequisites給的是邊資訊,prerequisites[i] = [
,
]表示存在一條從
指向
的邊,因此
是
的一個鄰接節點,其餘以此類推。
查詢環:在對圖的遍歷過程種,對於一個節點,其有三種狀態,0表示還未遍歷,1表示已經遍歷結束(所有鄰接節點都已遍歷),2表示正在遍歷(還有鄰接節點未遍歷),那麼如果在遍歷狀態為1的A節點時,再次遍歷到另一個狀態為1的B節點,就表示兩個節點之間存在環,這並不難理解。我們可以用一個長度為numCourses的int陣列來記錄每個節點的狀態。
具體程式碼如下:
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (prerequisites.length == 0 || prerequisites.length == 1) return true;
List<List<Integer>> adj = new ArrayList<>();
int[] visited = new int[numCourses];
// 新增節點,節點個數為numCourses
for (int i = 0; i < numCourses; i++) {
adj.add(new ArrayList<>());
}
// 新增鄰接節點
for (int i = 0; i < prerequisites.length; i++) {
adj.get(prerequisites[i][0]).add(prerequisites[i][1]);
}
// 遍歷圖中節點
for (int i = 0; i < numCourses; i++) {
if(findCycle(i, visited, adj)) return false;
}
return true;
}
public boolean findCycle(int node, int[] visited, List<List<Integer>> adj) {
// 0表示還未訪問
// 1表示已訪問過
// 2表示正在訪問
if (visited[node] == 2) return true; // 遍歷到某正在訪問的節點,找到環,返回true
if (visited[node] == 1) return false; // 遍歷到訪問結束的節點,未找到環,返回false
// 標記正在遍歷的節點
visited[node] = 2;
// 遍歷鄰接節點
for (Integer next : adj.get(node)) {
if(findCycle(next, visited, adj)) return true; //如果找到環,返回true
}
visited[node] = 1; // 標記訪問結束的節點
return false; // 未找到環,返回false
}
