Tarjan 演算法學習筆記

喵仔牛奶發表於2024-09-03

無向圖上的 Tarjan

定義:

  • \(dfn_u\):點 \(u\) 的時間戳,記錄點 \(u\) 是 DFS 過程中被訪問的時間。
  • \(low_u\):記錄點 \(u\) 經過至多一條非樹邊,所能達到的 dfn 最小的節點。

對於 DFS 中每一條遍歷到的邊 \((u,v)\)

  • 若搜尋樹上 \(v\)\(u\) 的兒子,即邊 \((u,v)\) 是樹邊,則有 \(low_u\gets \min(low_u, low_v)\)
  • 否則邊 \((u,v)\) 一定是返祖邊,即 \(v\)\(u\) 的祖先,則有 \(low_u=\min(low_u,dfn_v)\)
  • 可以發現對無向圖進行 DFS 遍歷時,是不可能存在橫叉邊的。

割點

一個點是割點的充要條件是:

  • 若點 \(u\) 為搜尋樹的根,且 \(u\) 有兩個以上的兒子,則 \(u\) 是割點。
  • 否則,如果存在 \(u\) 的一個兒子 \(v\),滿足 \(low_v\ge dfn_u\),則 \(u\) 是割點。

點雙連通分量

  • DFS 過程中,用棧來儲存被訪問過的點。
  • 對於點 \(u\),如果它的兒子 \(v\) 滿足 \(low_v\ge dfn_u\),則 \(u\) 是割點。不斷彈棧,直到彈出 \(v\);此時彈出的點和 \(u\) 就構成了一個點雙。
  • DFS 結束後,棧中剩下的點們構成一個點雙。
  • 割點會存在於多個點雙之中,其他點以及每一條邊只會存在一個點雙裡。

割邊

  • 首先非樹邊不可能成為割邊。
  • 對於樹邊 \((u,v)\),其中 \(u\)\(v\) 的父親;如果 \(low_v > dfn_u\),則邊 \((u,v)\) 是割邊。
  • 或者如果 \(low_u = dfn_u\),則 \(u\)\(u\) 的父親之間的樹邊是割邊。
  • 原理就是如果 \(v\) 無法透過返祖邊和 \(u\) 的祖先聯通,則 \((u,v)\) 是割邊。

邊雙連通分量

  • 同樣建立棧,將遍歷到的點依次壓入棧中。
  • 如果 \(low_u = dfn_u\),則 \(u\)\(u\) 的父親是割邊;則不斷彈棧,直到彈出點 \(u\)。然後將這些剛彈出的點歸入一個邊雙中。
  • 割邊不會存在於任何一個邊雙裡;其餘邊和所有點會恰好存在於一個邊雙裡。
  • 容易發現,縮完點後的圖會變成一棵優美的樹。

有向圖上的 Tarjan

定義:

  • \(dfn_u\):點 \(u\) 的時間戳,記錄點 \(u\) 是 DFS 過程中被訪問的時間。
  • \(low_u\):記錄點 \(u\) 經過至多一條返祖邊,所能達到的 dfn 最小的節點。

強連通分量

定義:

  • 如果兩個點 u,v,存在 u 到 v 的路徑,且存在 v 到 u 的路徑,則稱這兩個點強連通。
  • 所有點都強連通的圖稱為強連通圖。
  • 我們把圖中極大的強連通子圖稱為強連通分量。

求法:

  • 同樣開一個棧,將 dfs 到的點依次壓入棧裡面。
  • 每次更新 \(low_u\) 時,如果是橫叉邊且那個點無法回到 \(u\) 的某個祖先(即那個點不在棧裡面)則無視。
  • \(dfn_u=low_u\) 時,則不斷彈棧,直到彈出 \(u\),然後將這些點歸入一個 SCC 中。
  • 縮完點後的圖是個 DAG。

相關文章