資料結構——圖(9)——拓撲排序與DFS
DAG圖與AOV網
一個無環的有向圖稱為有向無環圖(DAG)。圖的頂點可以表示要執行的任務,並且邊可以表示一個任務必須在另一個之前執行的約束; 在這個應用程式中,拓撲排序只是任務的有效序列。 當且僅當圖形沒有有向迴圈時,即如果它是有向無環圖(DAG),則可以進行拓撲排序。 任何DAG都具有至少一個拓撲排序。這種用弧來表示活動之間的優先順序關係的有向圖,稱為頂點表示的活動的網(簡稱AOV網)。
拓撲排序(Topological sorting)
在電腦科學領域,有向圖的拓撲排序或拓撲排序是其頂點的線性排序。在圖中假設從頂點 i 到頂點 j 中有一條有向路徑 i -> j。那麼我們稱 i 為 j 的前驅,稱 j 為 i 的後繼。
拓撲排序的演算法非常簡單,常用的方式為Kahn’s algorithm演算法
- 在給定的有向圖中,選擇其中一個沒有前驅的頂點放入容器中
- 從圖中刪除該頂點及其所有的以其為尾的弧。
- 重複上述兩步,直至全部頂點輸出或者不存在沒有前驅的頂點為止。
該演算法的虛擬碼如下:
L ← 用來存放輸出的節點的列表 S ← 沒有前驅的所有點的集合 while S is non-empty do //當S不為空時 remove a node n from S //從集合S中移除節點N, add n to tail of L //並將節點N放在列表的尾部 //迴圈遍歷每一個從節點N到節點M的邊e for each node m with an edge e from n to m do //j將該邊從圖中移除 remove edge e from the graph //如果移除後節點m沒有以它為入度的邊,便將m插入到集合S中 if m has no other incoming edges then insert m into S //否則,圖中必定含有其他方向的邊 if graph has edges then //於是返回錯誤,表明此時圖中至少含有一個環 return error (graph has at least one cycle) else return L (a topologically sorted order)
下面用一個例項來說明一下拓撲排序的整個流程;
-
假定給我們一個有向圖如下圖所示:
找出它的拓撲排序。那麼我們可以這樣來。 -
先找沒有直接前驅的節點,在這裡V1,v6都沒有直接前驅,那麼任意選擇一個,並刪除所有以其為尾的弧。(假定我們選擇的是v6),那麼此時L的列表中的元素為 L = {v6}
-
重複上述步驟我們可以移除V1,得到L = {v6,v1}.
-
反覆如此,直到所有的頂點都輸出完畢或者不存在沒有前驅的頂點為止。整個流程如下:
由此我們可以看出拓撲排序的順序並不是唯一的。對於下面這幅圖就可以有多種拓撲順序:
利用DFS實現拓撲排序
當一個有向圖無環的時候,我們可以利用DFS演算法來實現拓撲排序。原理很簡單,由於圖中沒有環,那麼由圖中某點出發的時候,最先退出DFS的頂點一定是出度為0的頂點,也就是拓撲排序中最後的一個頂點(逆向思維)。因此按DFS退出的先後記錄下的頂點序列就是逆向的拓撲排序的序列。
具體的分析就不來了,虛擬碼貼上:
L ← Empty list that will contain the sorted nodes
while there are unmarked nodes do
select an unmarked node n
visit(n)
//定義這樣的visit函式
function visit(node n)
if n has a permanent mark then return
if n has a temporary mark then stop (not a DAG)
mark n temporarily
for each node m with an edge from n to m do
visit(m)
mark n permanently
add n to head of L
拓撲排序的應用
拓撲排序最常見的應用是基於其依賴性來排程一系列作業或任務。作業由頂點表示,如果在作業y開始之前必須完成作業x,那麼就存在從x到y的邊(例如,洗衣服時,洗衣機必須在我們將衣服放入烘乾機之前完成) 。然後,拓撲排序給出了執行作業的順序。 20世紀60年代早期,在PERT技術專案管理排程的背景下,首次研究了拓撲排序演算法的密切相關應用(Jarnagin 1960);在此應用程式中,圖形的頂點表示專案的里程碑,邊表示必須在一個里程碑與另一個里程碑之間執行的任務。拓撲排序構成了線性時間演算法的基礎,用於查詢專案的關鍵路徑,一系列里程碑和任務,用於控制整個專案進度的長度。
而對於上面4中的第二種情況我們可以知道,這個時候圖中的所有節點肯定是沒能全部輸出來的,因此我們可以得到一個判斷AOV網中是否存在環的判定方法:對於有向圖構造其頂點的拓撲序列,若網中的所有頂點都在該拓撲序列中,那麼該AOV網就不存在環。反之有環。這個結論學過計算機作業系統的人來說並不陌生。一個作業必須在另外一個作業完成之後才能執行,這在作業系統中對應程序間的同步關係。而我們常用前驅圖來表示其關係。而在程序間的關係中往往存在死鎖。而檢測死鎖的方法,就是拓撲排序法。(更多詳細內容去學習《作業系統》課程吧)