Joseph Chen
EP.13 學了 Graph DFS 的基礎:visited 集合防環、Grid 與 Adjacency List 兩種形式。這篇進入「有向圖」的核心問題:
「這些任務能不能被排出一個合法的完成順序?」
如果任務之間存在循環依賴(A 需要 B 先完成,B 需要 A 先完成),就無解。這就是拓撲排序(Topological Sort)要解決的問題,也是面試最常考的 Graph 進階題。
前置概念:In-degree(入度)
有向圖中,一個節點的「入度」是指有多少條邊指向它。 入度為 0 的節點代表「沒有任何前置條件」,可以最先被處理。
例:修課依賴圖
「機器學習」有兩條前置(線性代數+統計學),所以入度 = 2。 要學機器學習,必須先把入度降到 0。
Part 1 — Course Schedule
#207 · Medium · 能否完成所有課程?
題目
有 numCourses 門課,prerequisites[i] = [a, b] 代表「修 a 之前必須先修 b」。 判斷是否能完成所有課程(即依賴關係中是否存在環)。
numCourses=2, prerequisites=[[1,0]] → True(先修0再修1)
numCourses=2, prerequisites=[[1,0],[0,1]] → False(互相依賴,有環)
解法一:Kahn's Algorithm(BFS 拓撲排序)
核心思路:每次從圖中移除入度為 0 的節點,直到圖為空(成功)或卡住(有環)。
Kahn's 演算法步驟
建圖 + 計算每個節點的 in-degree
把所有 in-degree=0 的節點加入 queue
BFS:處理節點,更新鄰居 in-degree
最後檢查:處理的節點數是否等於 numCourses
Kahn's 演算法可視化
prerequisites = [[1,0],[2,1],[3,1]]
解法二:DFS 環偵測(三色標記法)
另一種思路:用 DFS 遍歷圖,用三種狀態標記每個節點——未訪問、正在訪問中(在當前 DFS 路徑上)、已完成。如果遇到「正在訪問中」的節點,代表有環。
Kahn's(BFS)適合用在
- • 需要輸出排序結果(Course Schedule II)
- • 邏輯直觀,容易理解
- • 不需要遞迴,無 stack overflow 風險
DFS 三色標記適合用在
- • 只判斷有無環,不需要排序
- • 面試偏好 DFS 的考官
- • 邏輯上更貼近「回溯路徑」的直覺
Part 2 — Course Schedule II
#210 · Medium · 輸出拓撲排序結果
和 #207 幾乎一樣,差別在於不只要回傳「能否完成」,還要回傳「一個合法的修課順序」。如果有環則回傳空陣列。
用 Kahn's Algorithm 的話,只要把 queue.popleft() 的節點順序收集起來就是答案。
拓撲排序通用模板
遇到「有依賴關係的任務排序」或「判斷是否有循環依賴」,直接套這個框架:
模板要注意的地方
• 邊的方向:要確認 graph 中 edge 的方向。Course Schedule 裡 [a, b] 代表「先修 b 才能修 a」,所以要建 b → a 的邊。
• in-degree 初始化:必須先遍歷所有邊,才能正確算出每個節點的入度。
• 結果長度判斷:len(result) == n 是唯一正確的有環判斷方式,不要另外另 check。
Graph 兩篇總結
| 題目 | Graph 類型 | 核心技術 | 關鍵資料結構 |
|---|---|---|---|
| Number of Islands | 無向 Grid | DFS / BFS 淹島 | set / 直接改值 |
| Clone Graph | 無向圖 | DFS + HashMap 防環 | dict{old: new} |
| Course Schedule | 有向圖(DAG 判斷) | Kahn's / DFS 三色 | indegree[], deque |
| Course Schedule II | 有向圖(拓撲排序) | Kahn's,收集順序 | indegree[], deque, result[] |
這篇學到什麼
上一篇
EP.13 — Graph 入門
Number of Islands、Clone Graph
下一篇
EP.15 — Backtracking
Subsets、Permutations、Combination Sum