拓樸排序是一種幫一張圖上的節點做線性排序的演算法。對於一張有向無環圖中的所有節點 ,排出一個序列,使所有有向邊 在序列中 必排在 之前
為什麼會需要這個演算法呢? 可以考慮以下問題:
已知資工系有 門課程:
課程 1 | 課程 2 | 課程 3 | 課程 4 | 課程 5 |
---|---|---|---|---|
計算機概論 | 電腦網路 | 離散數學 | 微積分 | 演算法 |
課程 6 | 課程 7 | 課程 8 | 課程 9 | 課程 10 |
---|---|---|---|---|
深度學習 | 機率論 | 資料結構 | 計算機組織 | 數位邏輯 |
每一門課程都有優先修習的順序,如下圖
以上圖為例,如果想要修習「演算法」,就得先修「資料結構」… 以此類推
如果某大一新生想排修課順序把 門課都修完,該如何排安排才能符合以上規則呢?
我們把問題拆解一下:
假設我只想修「資料結構」,那麼先決條件就是需要先修完「離散數學」與「計算機概論」
在此問題中,其中一種合理的順序是:
計算機概論、微積分、離散數學、數位邏輯、資料結構、計算機組織、演算法、電腦網路、機率論、深度學習
當然,我們也可以發現答案不只一種,像是以下也可以是一種解答:
微積分、計算機概論、離散數學、數位邏輯、資料結構、計算機組織、演算法、電腦網路、機率論、深度學習
這個問題相當簡單,看一下就能知道答案。因為它只有 門課,在圖論而言,就是 個節點
但是如果今天問題變成有 個節點呢? 這顯然需要一套「規則」去解決這個問題
拓樸排序可以為一張是 的圖 排出適當的走訪路徑,使對於 的所有邊 , 必出現在 之前
沒有,這一點拓樸學也沒有。在古早時代的「拓樸」一詞涵蓋的非常廣泛,基本上只要是非數字或幾何的東西都可能被冠上拓樸之稱。早期的圖論也被認為是拓樸學的內容,而現在則不一定。像是拓樸排序所排的節點順序就不是基於數字的大小關係,因此才會有「拓樸」二字出現
對於 的所有邊 , 必出現在 之前。因此必須最先選擇入度為 的節點,因為入度為 的節點沒有任何邊通往此節點
接下來要走訪鄰居節點,但是要怎麼走訪呢? 可以試著把已走訪過的節點從圖上「刪除」,如此一來,就會有新的入度為 的節點了
以下圖為例
我們可以將 刪除,如此一來就知道接下來要走訪 與 ,因為 與 的入度為
⭣
入度: 對於一有向圖,設一節點 ,則 的入度即為所有通往 的邊數
在先前探討 DFS 與 BFS 中,有提及 DFS 的「時間戳記」性質
首先,這個演算法名稱是這樣唸
step1. 第一個點必不會排在其他點後方,因此要找的 必須 ,即入度為
step2. 將每個入度為 的節點走訪過
step3. 刪除每個入度為 的節點,此步驟會產生新的入度為 的節點
step4. 回到 step1. 直到沒有入度為 的節點產生
這想法看起來非常遞迴,實際上用迴圈時做即可,比較看得懂
此方法也可以用於測定是否有環
假設這張有向圖是有環的,則會有可能產生「無法走訪」的節點。因此如果在跑完此演算法之後,發現有節點未被走訪,則這是一張有環有向圖
⭣ 完成拓樸排序後 ⭣
由於沒有任何節點入度為 ,因此演算法停止,然而仍有三點未被刪去,因此認定此圖有環
要特別說明一點,要排出順序必須要有「大小關係」
在 DFS 樹或是森林中:
對有向圖 進行 DFS 不產生後向邊,若且為若, 沒有迴路
假設以 DFS 走訪 ,若有一條邊 ,則 ,證明如下:
當 邊被發現時
得證: 對於 中的所有邊 而言,
由於每個節點都有「大小關係」,因此可以排出優先順序
根據以上證明,當一條邊 被發現時,如果 是灰點,使 為一條後向邊,則此圖有環
在實作上,不需要真的把節點刪除,只需要在走訪節點時將其鄰居的入度 即可
其實 queue 改成 stack 也會產生一組拓樸排序的結果
令節點數 、邊數
令節點數 、邊數
Zerojudge f167. m4a1-社團 Club
CSES Course Schedule
UVa 200 - Rare Order
CSES Game Routes
CSES Longest Flight Route (DP on DAG)
AtCoder dp_g - G - Longest Path (DP on DAG)
Zerojudge k734. APCS 4. 開啟寶盒
Codeforces Contest 1385 E. Directing Edges
Zerojudge l245. D. 汽車不再繞圈圈 (二分搜 + 拓排 + 把DFS版本的證明讀熟就會的東西)
ShanC 程式競賽筆記
作者: ShanC
更新: 2024/11/22