Tarjan強連通分量詳解補充

來源:互聯網
上載者:User
statement

這篇部落格引用了其他部落格的資料,我自己補充了一點。好文必須轉切補充。。

說到以Tarjan命名的演算法,我們經常提到的有3個,其中就包括本文所介紹的求強連通分量的Tarjan演算法。而提出此演算法的普林斯頓大學的Robert E Tarjan教授也是1986年的圖靈獎獲得者(具體原因請看本博“曆屆圖靈獎得主”一文)。 首先明確幾個概念

補充:
1.割點:若刪掉某點後,原連通圖分裂為多個子圖,則稱該點為割點。

2.割點集合:在一個無向連通圖中,如果有一個頂點集合,刪除這個頂點集合,以及這個集合中所有頂點相關聯的邊以後,原圖變成多個連通塊,就稱這個點集為割點集合。

3.點連通度:最小割點集合中的頂點數。

4.割邊(橋):刪掉它之後,圖必然會分裂為兩個或兩個以上的子圖。

5.割邊集合:如果有一個邊集合,刪除這個邊集合以後,原圖變成多個連通塊,就稱這個點集為割邊集合。

6.邊連通度:一個圖的邊連通度的定義為,最小割邊集合中的邊數。

7.縮點:把沒有割邊的連通子圖縮為一個點,此時滿足任意兩點之間都有兩條路徑可達。 註:求塊<>求縮點。縮點後變成一棵k個點k-1條割邊串連成的樹。而割點可以存在於多個塊中。

8.雙連通分量:分為點雙連通和邊雙連通。它的標準定義為:點連通度大於1的圖稱為點雙連通圖,邊連通度大於1的圖稱為邊雙連通圖。通俗地講,滿足任意兩點之間,能通過兩條或兩條以上沒有任何重複邊的路到達的圖稱為雙連通圖。無向圖G的極大雙連通子圖稱為雙連通分量。

詳解:
1、強連通圖。在一個強連通圖中,任意兩個點都通過一定路徑互相連通。比如圖一是一個強連通圖,而圖二不是。因為沒有一條路使得點4到達點1、2或3。

2、強連通分量。在一個非強連通圖中極大的強連通子圖就是該圖的強連通分量。比如圖三中子圖{1,2,3,5}是一個強連通分量,子圖{4}是一個強連通分量。

Tarjan演算法的應用論述:

1.求強連通分量、割點、橋、縮點:
對於Tarjan演算法中,我們得到了dfn和low兩個數組,
low[u]:=min(low[u],dfn[v])——(u,v)為後向邊,v不是u的子樹; low[u]:=min(low[u],low[v])——(u,v)為樹枝邊,v為u的子樹;

下邊對其進行討論:
若low[v]>=dfn[u],則u為割點,u和它的子孫形成一個塊。
因為這說明u的子孫不能夠通過其他邊到達u的祖先,這樣去掉u之後,圖必然分裂為兩個子圖。
若low[v]>dfn[u],則(u,v)為割邊。理由類似於上一種情況。

其實,tarjan演算法的基礎是DFS。我們準備兩個數組Low和Dfn**。Low數組是一個標記數組,記錄該點所在的強連通子圖所在搜尋子樹的根節點的Dfn值(很繞嘴,往下看你就會明白),**Dfn數組記錄搜尋到該點的時間,也就是第幾個搜尋這個點的。根據以下幾條規則,經過搜尋遍曆該圖(無需回溯)和對棧的操作,我們就可以得到該有向圖的強連通分量。

數組的初始化:當首次搜尋到點p時,Dfn與Low數組的值都為到該點的時間。
堆棧:每搜尋到一個點,將它壓入棧頂。
當點p有與點p’相連時,如果此時(時間為dfn[p]時)p’不在棧中,p的low值為兩點的low值中較小的一個。
當點p有與點p’相連時,如果此時(時間為dfn[p]時)p’在棧中,p的low值為p的low值和p’的dfn值中較小的一個。
每當搜尋到一個點經過以上操作後(也就是子樹已經全部遍曆)的low值等於dfn值,則將它以及在它之上的元素彈出棧。這些出棧的元素組成一個強連通分量。

繼續搜尋(或許會更換搜尋的起點,因為整個有向圖可能分為兩個不連通的部分),直到所有點被遍曆。

由於每個頂點只訪問過一次,每條邊也只訪問過一次,我們就可以在O(n+m)的時間內求出有向圖的強連通分量。但是,這麼做的原因是什麼呢。 Tarjan演算法的操作原理如下:

Tarjan演算法基於定理:在任何深度優先搜尋中,同一強連通分量內的所有頂點均在同一棵深度優先搜尋樹中。也就是說,強連通分量一定是有向圖的某個深搜樹子樹。
可以證明,當一個點既是強連通子圖Ⅰ中的點,又是強連通子圖Ⅱ中的點,則它是強連通子圖Ⅰ∪Ⅱ中的點。
這樣,我們用low值記錄該點所在強連通子圖對應的搜尋子樹的根節點的Dfn值。注意,該子樹中的元素在棧中一定是相鄰的,且根節點在棧中一定位於所有子樹元素的最下方。
強連通分量是由若干個環組成的。所以,當有環形成時(也就是搜尋的下一個點已在棧中),我們將這一條路徑的low值統一,即這條路徑上的點屬於同一個強連通分量。
如果遍曆完整個搜尋樹後某個點的dfn值等於low值,則它是該搜尋子樹的根。這時,它以上(包括它自己)一直到棧頂的所有元素組成一個強連通分量。

Tarjan求有向圖強連通分量、割點、割邊的代碼: Var     n,m,i,j,x,y,z:longint;     a,b:array[0..1000,0..1000]of longint;//圖     dfn,low,s:array[0..1000]of longint;//dfn為時間戳記,low為祖先,s為棧                       vis,ins:array[0..1000]of boolean;//vis為是否訪問,ins為是否在棧中         num,p:longint; function min(x,y:longint):longint;  begin     if x<y then exit(x) else exit(y); end; procedure tarjan(u:longint);  var       i,v:longint;  begin     inc(num);//給定一個時間戳記       dfn[u]:=num;       low[u]:=num;       vis[u]:=true;       inc(p);//入棧     s[p]:=u;     ins[u]:=true;     for i:=1 to b[u,0] do//注意只有u與i相連才進行下面的操作        if not vis[b[u,i]] then//未被訪問     begin         tarjan(b[u,i]);         low[u]:=min(low[u],low[b[u,i]]);//是樹枝邊,取兩個low的min值        {如果是求割點或者割邊,在這裡判斷dfn[u]和low[v]的大小並進行彈棧即可。}            end     else     if ins[b[u,i]] then//在棧中         low[u]:=min(low[u],dfn[b[u,i]]);//非樹枝邊,取low與dfn的min值          if dfn[u]=low[u] then//已經找到一個強連通分量,彈棧。        repeat              v:=s[p];             write(v,' ');              ins[v]:=false;              dec(p);          if u=v then writeln;        until u=v;  end; begin      readln(n,m);      for i:=1 to m do//構圖       begin          readln(x,y);             inc(b[x,0]);          b[x,b[x,0]]:=y;       end;     tarjan(1); End. 

2.求雙連通分量以及構造雙連通分量:
對於點雙連通分支,實際上在求割點的過程中就能順便把每個點雙連通分支求出。
建立一個棧,儲存當前雙連通分支,在搜尋圖時,每找到一條樹枝邊或後向邊(非橫叉邊),就把這條邊加入棧中。如果遇到某時滿足DFS(u)<=Low(v),說明u是一個割點,同時把邊從棧頂一個個取出,直到遇到了邊(u,v),取出的這些邊與其關聯的點,組成一個點雙連通分支。

割點可以屬於多個點雙連通分支,其餘點和每條邊只屬於且屬於一個點雙連通分支。
對於邊雙連通分支,求法更為簡單。只需在求出所有的橋以後,把橋邊刪除,原圖變成了多個連通塊,則每個連通塊就是一個邊雙連通分支。橋不屬於任何一個邊雙連通分支,其餘的邊和每個頂點都屬於且只屬於一個邊雙連通分支。

一個有橋的連通圖,如何把它通過加邊變成邊雙連通圖。

方法為首先求出所有的橋,然後刪除這些橋邊,剩下的每個連通塊都是一個雙連通子圖。把每個雙連通子圖收縮為一個頂點,再把橋邊加回來,最後的這個圖一定是一棵樹,邊連通度為1。

統計出樹中度為1的節點的個數,即為分葉節點的個數,記為leaf。
則至少在樹上添加(leaf+1)/2條邊,就能使樹達到邊二連通,所以至少添加的邊數就是(leaf+1)/2。
具體方法為,首先把兩個最近公用祖先最遠的兩個分葉節點之間串連一條邊,這樣可以把這兩個點到祖先的路徑上所有點收縮到一起,因為一個形成的環一定是雙連通的。然後再找兩個最近公用祖先最遠的兩個分葉節點,這樣一對一對找完,恰好是(leaf+1)/2次,把所有點收縮到了一起。

3.求最近公用祖先(LCA)
在遍曆到u時,先tarjan遍曆完u的子樹,則u和u的子樹中的節點的最近公用祖先就是u,並且u和【u的兄弟節點及其子樹】的最近公用祖先就是u的父親。注意到由於我們是按照DFS順序遍曆的,我們可用一個color數組標記,正在訪問的染色為1,未訪問的標記為0,已經訪問到即在【u的子樹中的】及【u的已訪問的兄弟節點及其子樹中的】染色標記為2,這樣我們可以通過並查集的不斷合并更新,通過find實現以上目標。 Code

function find(x:longint):longint;   begin     if f[x]<>x then f[x]:=find(f[x]);     end; procedure tarjan(u:longint);   begin      f[u]:=u;      for i:=1 to n do      if (g[u,i])and(color[i]=0) then//g[u,i]表示u連著i      begin             f[i]:=u;              end;      for i:=1 to n do      if ((ask[u,i])or(ask[i,u]))and(color[i]=2) then//ask[u,i]表示詢問了u,i             begin          lca[u,i]:=find(i);         lca[i,u]:=lca[u,i];             end;      color[u]:=2;   end;

一道例題(題號我忘了囧) CODE

#include <cstdio>#include <iostream>#define N 50001#define LL long long#define fo(i,a,b) for (int i=a;i<=b;i++)using namespace std;int dfn[N],low[N],zhan[N];bool v[N];int t[2*N],next[2*N],last[2*N];int n,m,l,top=0;LL ans=0;void add(int x,int y){    t[++l]=y;    next[l]=last[x];    last[x]=l;}void tarjan(int x){    zhan[++top]=x;    dfn[x]=++l;    low[x]=l;    int k=last[x];  //  bz[x]=true;    v[x]=true;    while (k!=0)    {        if (dfn[t[k]]==0)        {            tarjan(t[k]);            if (low[t[k]]<low[x]) low[x]=low[t[k]];        }        else if (v[t[k]] && dfn[t[k]]<low[x]) low[x]=dfn[t[k]];        k=next[k];    }    if (low[x]==dfn[x])    {        LL xdl=0;        while (zhan[top+1]!=x)        {            v[zhan[top]]=false;            top--;            xdl++;        }        ans+=(xdl-1)*xdl/2;    }}int main(){    scanf("%d%d",&n,&m);    int x,y;    fo(i,1,m)    {        scanf("%d%d",&x,&y);        add(x,y);    }    l=0;    fo(i,1,n)    if (not bz[i]) tarjan(i);    printf("%d",ans);}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.