標籤:
一、強連通分量
在有向圖G中,如果任意兩個不同的頂點相互可達,則該有向圖為強連通的;一個有向圖G的極大連通子圖稱為G的強連通分支;將有向圖G的每一條邊反向形成的圖稱為G的轉置 Gt.
1.1 有向圖的一些定理
-
有向非循環圖中唯一出度為0的點,一定可以由任何點出發到達
圖中節點的數目為有限多個,而且無環,因此從任何點出發往前走,一定終止於出度為0的點,否則走N+1步(N為圖中節點的數目),則必然有一個點被走了兩次,形成了環,與無環矛盾!
-
有向非循環圖中所有入度不為0的點,一定可以由某個入度為0的點出發可達
由於無環,所以從任何入度不為0的點往回走,必然終止於一個入度為0的點。
1.2 強連通分量的求解
一個有向圖G可以存在多個極大連通子圖,即多個強連通分支。求一個有向圖的強連通分支的方法主要有:Korasaju演算法和Tarjan演算法。
1.2.1 Korasaju演算法
1. 深度優先遍曆圖G,演算法每個節點u的結束時間f[u],起點如何選擇無所謂;
2. 深度優先遍曆G的轉置圖Gt,選擇遍曆的起點的時候,按照節點的結束時間從大到小進行。遍曆的過程中,一邊遍曆,一邊給節點做分類標記,每找到一個新起點,分類標記就加1;
3. 第2步中產生的標記值相同的節點構成深度優先森林中的一棵樹,也即一個強連通分量
1.2.2 Tarjan演算法
1. 做一遍DFS,用dfn[i]表示編號為i的節點在DFS過程中的訪問序號(也可以叫做開始時間)。DFS過程中會形成一棵搜尋樹,在搜尋樹上越先遍曆到的節點,dfn的值就越小。dfn值越小的節點,就稱為越“早”。
2. low[i]表示從i節點出發DFS過程中i下方節點(開始時間大於dfn[i],且由i可達的節點)所能到達的最早的節點的開始時間。初始時 low[i] = dfn[i].
3. DFS過程中,碰到哪個節點,就將哪個節點入棧。棧中節點只有在其所屬的強連通分量已經全部求出時,才會出棧。
4. 如果發現某節點u有邊串連到棧裡的節點v,則更新u的low值為 min(low[u], dfn[v])。若low[u]被更新為dfn[v],則表明目前發現u可達的最早的節點是v。
(1)對於u的子節點v,從v出發進行的DFS結束回到u時,使得low[u] = min(low[u], low[v])。因為u可達v,所以v可達的最早的節點,也是u可達的。
(2)如果一個節點u,從其出發進行的DFS已經全部完成並回到u,而且此時 low[u] == dfn[u],說明u可達的所有節點,都不能到達任何比u早的節點——那麼,該節點u就是一個強連通分量在DFS搜尋樹中的根。此時,顯然棧中u上方的節點,都是不能到達比u早的節點的。將棧中節點彈出,一直彈到u(包括u),彈出的節點就構成了一個強連通分量。
void Tarjan(int u){ dfn[u] = low[u] = ++index; stack.push(u); for each(u,v) in E{ if (v is not visited){ Tarjan(v); low[u] = min(low[u], low[v]); }else if (v in stack){ low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]){ //u是一個強連通分量的根 repeat v = stack.pop print v until (u == v) }//退棧,把整個強連通分量都彈出來}
二、割點和橋
無向連通圖中,如果刪除某點後,圖變成不連通,則稱該點為割點
無向連通圖中,如果刪除某邊後,圖變成不連通,則稱該邊為橋
求割點和橋的Tarjan演算法
思路和求有向圖的強連通分量類似,在深度優先遍曆整個圖過程中形成一棵搜尋樹。定義dfn[u]為節點u第一次被訪問到的時間(序號),low[u]為u或者u的子樹中能夠通過非父子邊(父子邊就是搜尋樹上的邊)追溯到的最早的節點的DFS開始時間。
一個頂點u是割點,若且唯若滿足(1)或(2)
(1)u為樹根,且u有多於一個子樹
(2)u不為樹根,且存在(u,v)為樹枝邊(或稱父子邊,即u為v在搜尋樹中的父親),使得 dfn(u) <= low(u)
一條邊(u,v)是橋,若且唯若
(u,v)為樹枝邊,且滿足dfn(u) < low(v)。注意,(u,v)不能有重邊。
Tarjan演算法在DFS過程中判斷割點或橋
void Tarjan(u){ dfn[u] = low[u] = ++index; for each(u,v) in E{ if (v is not visited){ Tarjan(v) low[u] = min(low[u], low[v]) if(d[u] < low[v]) (u,v)為橋 }else if (v 不是 u的父節點){ low[u] = min(low[u], dfn[v]); } } if (u 為根) u是割點 <=> u 有至少兩個子節點 else u是割點 <=> u 有一個子節點v,滿足 dfn[u] <= low[v]}
Tarjan演算法之後判斷割點或橋
可以先用Tarjan進行dfs算出所有點的low和dfn值,並記錄dfs過程中每個點的父節點,然後再把所有點看一遍,看起low和dfn,以找出割點和橋。
找橋的時候,需要注意有無重邊,若有重邊,則不是橋。
三、無向連通圖點雙連通分支
無向連通圖的點雙連通分支是指不包含割點的極大連通子圖。
1. 建立一個棧,儲存當前雙連通分支,搜尋圖時,每找到一條樹枝邊或反向邊(連到樹中祖先的邊),就把這條邊加入棧中。
2. 如果遇到某樹枝邊(u,v)滿足dfn[u] <= low[v],說明u是一個割點,此時把邊從棧頂一個個取出,直到遇到了邊(u,v),取出的這些邊與其關聯的點,組成一個點雙連通分支。
3. 割點可以屬於多個點雙連通分支,其餘點和每條邊只能屬於一個點雙連通分支
四、無向連通圖邊連通分支
無向連通圖的邊雙連通分支是指不包含橋的極大連通子圖。
求無線連通圖的邊連通分支,只需要在求出所有的橋以後,把橋邊刪除,原圖就變成了多個連通塊,則每個連通塊就是一個邊連通分支。
橋不屬於任何一個邊連通分支,其餘的邊和每個頂點都屬於且只屬於一個邊連通分支。
強連通分量、割點、橋