圖的割點算法、圖的割邊算法
割點算法
• 在一個無向連通圖中,如果刪除某個頂點後,圖不再連通(即任意兩點之間不能相互到達),我們稱這樣的頂點為割點(或者稱割頂)。
判斷一個頂點是不是割點除了從定義,還可以從DFS(深度優先遍歷)的角度出發。我們先通過DFS定義兩個概念。
假設DFS中我們從頂點U訪問到了頂點V(此時頂點V還未被訪問過),那麽我們稱頂點U為頂點V的父頂點,V為U的孩子頂點。在頂點U之前被訪問過的頂點,我們就稱之為U的祖先頂點。
顯然如果頂點U的所有孩子頂點可以不通過父頂點U而訪問到U的祖先頂點,那麽說明此時去掉頂點U不影響圖的連通性,U就不是割點。相反,如果頂點U至少存在一個孩子頂點,必須通過父頂點U才能訪問到U的祖先頂點,那麽去掉頂點U後,頂點U的祖先頂點和孩子頂點就不連通了,說明U是一個割點。
上圖中的箭頭表示DFS訪問的順序(而不表示有向圖),對於頂點D而言,D的孩子頂點可以通過連通區域1紅色的邊回到D的祖先頂點C(此時C已被訪問過),所以此時D不是割點。
上圖中的連通區域2中的頂點,必須通過D才能訪問到D的祖先頂點,所以說此時D為割點。再次強調一遍,箭頭僅僅表示DFS的訪問順序,而不是表示該圖是有向圖。
這裏我們還需要考慮一個特殊情況,就是DFS的根頂點(一般情況下是編號為0的頂點),因為根頂點沒有祖先頂點。其實根頂點是不是割點也很好判斷,如果從根頂點出發,一次DFS就能訪問到所有的頂點,那麽根頂點就不是割點。反之,如果回溯到根頂點後,還有未訪問過的頂點,需要在鄰接頂點上再次進行DFS,根頂點就是割點。
Tarjan算法的實現細節
在具體實現Tarjan算法上,我們需要在DFS(深度優先遍歷)中,額外定義兩個數組dfn[],low[]。
4.1 num數組
num數組的下標表示頂點的編號,數組中的值表示該頂點在DFS中的遍歷順序(或者說時間戳),每訪問到一個未訪問過的頂點,訪問順序的值(時間戳)就增加1。子頂點的num值一定比父頂點的num值大(但不一定恰好大1,比如父頂點有兩個及兩個以上分支的情況)。在訪問一個頂點後,它的dfn的值就確定下來了,不會再改變。
4.2 low數組
low數組的下標表示頂點的編號,數組中的值表示DFS中該頂點不通過父頂點能訪問到的祖先頂點中最小的順序值(或者說時間戳)。
每個頂點初始的low值和dfn值應該一樣,在DFS中,我們根據情況不斷更新low的值。
假設由頂點U訪問到頂點V。當從頂點V回溯到頂點U時,
如果
num[v] < low[u]
那麽
low[u] = num[v]
如果頂點U還有它分支,每個分支回溯時都進行上述操作,那麽頂點low[u]就表示了不通過頂點U的父節點所能訪問到的最早祖先節點。
4.4 一個具體的例子
現在我們來看一個例子,模仿程序計算各個頂點的num值和low值。下圖中藍色實線箭頭表示已訪問過的路徑,無箭頭虛線表示未訪問路徑。已訪問過的頂點用黃色標記,未訪問的頂點用白色標記,DFS當前正在處理的頂點用綠色表示。帶箭頭的藍色虛線表示DFS回溯時的返回路徑。
基本思路:
假如我們在dfs時訪問到了u點,此時圖就會被u點分割成為兩部分。一部分是已經被訪問過的點,另一部分是沒有被訪問過的點。如果u點是割點,那麽剩下的沒有被訪問過的點中至少有一個點在不經過u點的情況下,是無論如何再也回不到已經訪問過的點了。假如到了u後,圖中還有頂點v是沒有訪問過的點,如何判斷v在不經過u的情況下是否還能回到之前訪問過的任意一個點?u是v的父親,而之前訪問過的頂點就是祖先。也就是如何檢測v在不經過父親u的情況下還能否回到祖先。那就是對v再進行一次dfs,但此次遍歷不經過u,看能否回到祖先。不能u即為割點。
我們需要一個數組low來記錄每個頂點在不經過父頂點時,能夠回到的最小“時間戳”。
對於某個頂點u,如果存在至少一個頂點v(u的兒子),使得low[v]>=num[u],即不能回到祖先,那麽u點為割點。
參考資料
[1] 小地盤, 圖的割點算法vs割邊算法
[2] Trent, 求無向圖聯通圖的割點
[3] GeeksforGeeks, Articulation Points (or Cut Vertices) in a Graph.
圖的割點算法、圖的割邊算法