207. 課程表(拓撲排序bfs)
你這個學期必須選修 numCourses
門課程,記為 0
到 numCourses - 1
。
在選修某些課程之前需要一些先修課程。 先修課程按陣列 prerequisites
給出,其中 prerequisites[i] = [ai, bi]
,表示如果要學習課程 ai
則 必須 先學習課程 bi
。
- 例如,先修課程對
[0, 1]
表示:想要學習課程0
,你需要先完成課程1
。
請你判斷是否可能完成所有課程的學習?如果可以,返回 true
;否則,返回 false
。
示例 1:
輸入:numCourses = 2, prerequisites = [[1,0]] 輸出:true 解釋:總共有 2 門課程。學習課程 1 之前,你需要完成課程 0 。這是可能的。
示例 2:
輸入:numCourses = 2, prerequisites = [[1,0],[0,1]] 輸出:false 解釋:總共有 2 門課程。學習課程 1 之前,你需要先完成課程 0 ;並且學習課程 0 之前,你還應先完成課程 1 。這是不可能的。
題意解釋
一共有 n 門課要上,編號為 0 ~ n-1。
先決條件[1, 0],意思是必須先上課 0,才能上課 1。
給你 n 、和一個先決條件表,請你判斷能否完成所有課程。
再舉個生活的例子
先穿內褲再穿褲子,先穿打底再穿外套,先穿衣服再戴帽子,是約定俗成的。
內褲外穿、光著身子戴帽子等,都會有點奇怪。
我們遵循穿衣的一條條先後規則,用一串順序行為,把衣服一件件穿上。
我們遵循課程之間的先後規則,找到一種上課順序,把所有課一節節上完。
用有向圖描述依賴關係
示例:n = 6,先決條件表:[[3, 0], [3, 1], [4, 1], [4, 2], [5, 3], [5, 4]]
課 0, 1, 2 沒有先修課,可以直接選。其餘的課,都有兩門先修課。
我們用有向圖來展現這種依賴關係(做事情的先後關係):
這種叫 有向無環圖,把一個 有向無環圖 轉成 線性的排序 就叫 拓撲排序。
有向圖有 入度 和 出度 的概念:
如果存在一條有向邊 A --> B,則這條邊給 A 增加了 1 個出度,給 B 增加了 1 個入度。
所以,頂點 0、1、2 的入度為 0。頂點 3、4、5 的入度為 2。
每次只能選你能上的課
每次只能選入度為 0 的課,因為它不依賴別的課,是當下你能上的課。
假設選了 0,課 3 的先修課少了一門,入度由 2 變 1。
接著選 1,導致課 3 的入度變 0,課 4 的入度由 2 變 1。
接著選 2,導致課 4 的入度變 0。
現在,課 3 和課 4 的入度為 0。繼續選入度為 0 的課……直到選不到入度為 0 的課。
這很像 BFS
讓入度為 0 的課入列,它們是能直接選的課。
然後逐個出列,出列代表著課被選,需要減小相關課的入度。
如果相關課的入度新變為 0,安排它入列、再出列……直到沒有入度為 0 的課可入列。
BFS 前的準備工作
每門課的入度需要被記錄,我們關心入度值的變化。
課程之間的依賴關係也要被記錄,我們關心選當前課會減小哪些課的入度。
因此我們需要選擇合適的資料結構,去存這些資料:
入度陣列:課號 0 到 n - 1 作為索引,通過遍歷先決條件表求出對應的初始入度。
鄰接表:用雜湊表記錄依賴關係(也可以用二維矩陣,但有點大)
key:課號
value:依賴這門課的後續課(陣列)
怎麼判斷能否修完所有課?
BFS 結束時,如果仍有課的入度不為 0,無法被選,完成不了所有課。否則,能找到一種順序把所有課上完。
或者:用一個變數 count 記錄入列的頂點個數,最後判斷 count 是否等於總課程數。
作者:xiao_ben_zhu
連結:https://leetcode.cn/problems/course-schedule/solution/bao-mu-shi-ti-jie-shou-ba-shou-da-tong-tuo-bu-pai-/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
1 class Solution { 2 public: 3 bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { 4 5 unordered_map<int,std::vector<int>> map; 6 vector<int> ingreed(numCourses,0); 7 for(int i = 0; i < prerequisites.size();i++) { 8 ingreed[prerequisites[i][0]]++; 9 map[prerequisites[i][1]].emplace_back(prerequisites[i][0]); 10 } 11 queue<int> q; 12 for(int i = 0; i < ingreed.size();i++) { 13 if (ingreed[i]==0) { 14 q.push(i); 15 } 16 } 17 int cnt = 0; 18 while(!q.empty()) { 19 int top = q.front(); 20 q.pop(); 21 cnt++; 22 for(auto adj: map[top]) { 23 ingreed[adj]--; 24 if (ingreed[adj]==0) { 25 q.push(adj); 26 } 27 } 28 } 29 return cnt == numCourses; 30 } 31 };