1. 程式人生 > >tarjan演算法入門(一)——割點割邊(未完成割點部分)

tarjan演算法入門(一)——割點割邊(未完成割點部分)

一.概述.

tarjan演算法是Robert tarjan發明的一種基於深度優先遍歷的演算法,這種演算法可以求無向圖的割點(割頂)割邊(橋),進一步可以用於求無向圖的雙連通分量;在有向圖方面可以求出強連通分量等.

二.割點與割邊.

割點與割邊是圖論中重要的概念.

割點的定義:在一張無向圖中,去掉一個點,可以將這個點所在的聯通塊分成兩個或多個聯通塊,則稱這個點是這張無向圖的割點.

割邊的定義:在一張無向圖中,去掉一條邊,可以將這條邊所在的聯通塊分成兩個或多個聯通塊,則稱這個點是這張無向圖的割邊.

三.tarjan演算法.

關於tarjan演算法的定義,本人也不是很清楚,可能就是一類用到了生成樹或者dfn與low值的深度優先遍歷演算法吧.

運用tarjan演算法求割點與割邊首先要了解dfn與low值.

dfn值:dfn值其實就是時間戳,深度優先遍歷時一個點i的dfn值就是一個點被遍歷到的順序(即被遍歷到之前有幾個節點被遍歷過).

low值:

我們dfs遍歷出來的是一棵生成樹,在這棵生成樹上的邊我們稱作樹邊,其它邊我們稱作非樹邊.

那麼一個點的low值即為一個點通過由非樹邊組成的路徑能夠達到的dfn值最小的點的dfn值.

定理1:對於任意一條邊,這條邊連線的兩點在生成樹上一定是祖先與後代的關係.

證明如下:

若一條邊連線的兩點在生成樹上不是祖先與後代,那麼dfs是應該會從先dfs到的那個點dfs到另一個點,與假設相矛盾.

證畢.

那麼我們現在就可以搬出定理2了.

定理2:若一條邊是割邊,則這條邊只會出現在生成樹上,且這條邊連線的父親的dfn值要大於兒子的low值.

證明如下:

若這條邊是非樹邊,則去掉這條邊之後,生成樹一節連通,與割邊的定義相矛盾.

若這條邊的兒子的low值大於或等於了父親的dfn值,根據low值的定義,去掉這條邊後,這個兒子能夠連通到的dfn值最小的點要小於它的父親.

根據定理1,我們可以知道一條邊連線的點的關係一定是祖先與後代,所以這個兒子可以連通到祖先,聯通塊沒有被破壞,與割邊的定義相矛盾.

證畢.

那麼現在我們要做的就是在一個dfs內求出dfn值和low值,就可以得到割邊了.

那麼演算法流程如下:

1.進行dfs,先確定這個點的dfn值.

2.遍歷這個點的所有兒子.

3.每遍歷一個兒子後,利用定理2判定這個兒子到它的邊是否為割邊,並打上標記.

4.若一個兒子沒有被遍歷過,則將這個點當前的low值與它兒子的low值取min.

5.若這個點被遍歷過,則將這個點當前的low值與它兒子的dfn值取min.

那麼求割邊的模板如下:

void dfs(int k,int fa){
  dfn[k]=low[k]=++tt;
  for (int i=lin[k];i;i=e[i].next){
    int y=e[i].y;
    if (!dfn[y]) {
      dfs(y,k);
      low[k]=min(low[y],low[k]);
      if (low[y]>dfn[k]) e[i].br=e[i^1].br=1;
    }else if (y^fa){
      low[k]=min(dfn[y],low[k]);
    }
  }
}