1. 程式人生 > >求有向圖的強連通分支(Tarjan演算法)

求有向圖的強連通分支(Tarjan演算法)

強連通分支

如果兩個頂點可以相互通達,則稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。

Tarjan演算法

Tarjan演算法是用來求有向圖的強連通分量的。求有向圖的強連通分量的Tarjan演算法是以其發明者Robert Tarjan命名的。Robert Tarjan還發明瞭求雙連通分量的Tarjan演算法。

流程

對有向圖先任取一個節點,只要這個節點沒被遍歷過,則從其開始進行深度優先遍歷。這裡記錄兩個陣列,dfn[i] and low[i]

,其中dfn[i]很容易理解,就是在是深度遍歷的時候,遍歷到第i個節點的時候,當前是第幾個遍歷到的節點,也就是說這個遍歷序列的位置。第一個遍歷到就是1,遍歷到第10個節點的時候,遍歷到就是10,相當於時間戳。

還有low[i],這個就比較難理解了,其實這個記錄的是,當前這個節點i最遠能到之前哪個節點, 例如在圖裡面有一個環:1->2->3->4->1那麼low[4] = dfu[1],但是假如還包括一條變4->2,那麼指向誰呢? 答:指向小的那個! 也就是找最大的強連通分支。

例子

首先我們看這個圖,先用肉眼找一下強連通分支,分別是:{1,2,3,4}, {5}, {6}

這三個強連通分支

圖

那麼對圖開始遍歷,對於深度遍歷,首先從1開始,一直遍歷到6.
發現6到頭了,然後比較當前節點是不是一個強連通的分支的根( dfu[i] == low[i] 就是),發現是的,彈棧知道剛好6彈出棧。所彈出棧的所有節點,就是第一個子強連通圖:{6}

遍歷

對於5 我們得到low[5] = min(low[5], low[6]),這樣發現,5也是一個根,則彈棧得到:{5},然後彈棧到3,得到low[3] = min(low[3], low[5]) = 2

遍歷

然後3號節點走到4號節點,繼續走到1號節點,發現1號已經在棧裡面了,說明找到一個強連通分支了,那麼讓low[4] = min( low[4], dfn[1] )

這裡是意思是讓low[4]最小,也就能找到最大的環。4走完了之後(不走6 因為6是已經遍歷過的節點,並且不在棧裡面),彈棧,到33發現也走完了,得到low[3] = min(low[3], low[4]) = 1,然後彈棧到1,low[1] = min(low[1], low[3])

遍歷

再然後遍歷節點2,遍歷到4的時候發現,也已經遍歷過了,但是4在棧裡面,那麼low[2] = min(low[2], dfn[4]) = 5
遍歷

然後深度遍歷完了,回退到1號節點,發現dfn[1] == low[1]就開始彈棧,得到第三個強連通分支:{1, 2, 3, 4}

程式碼

#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <stack>


using namespace std;

#ifndef MAXNODE
#define MAXNODE 20005
#endif

int flag[MAXNODE] = {0}; //判斷圖中的點是否在棧內
int dfn[MAXNODE] = {0}; //dfu陣列
int low[MAXNODE] = {0}; //low陣列
int tpArr[MAXNODE] = {0}; //存放節點屬於第幾組聯通圖
int tpNum = 0; //對強連通分支設定ID
int _index = 0; //dfs遍歷的ID

vector<int> graph[MAXNODE]; //圖

stack<int> st; //棧

void tarjan(int n){
    st.push(n);
    flag[n] = 1;
    dfn[n] = ++_index;
    low[n] = _index;
    for (int i = 0; i < graph[n].size(); i++) {
        int cur = graph[n][i];
        if (dfn[cur] == 0) {
            tarjan(cur);
            low[n] = min(low[n], low[cur]);
        }else{
            if (flag[cur] == 1) {
                low[n] = min(low[n], dfn[cur]);
            }
        }
    }
    if (dfn[n] == low[n]) {
        tpNum++;
        do{
            n = st.top();
            st.pop();
            flag[n] = 0;
            tpArr[n] = tpNum;
        }while(dfn[n] != low[n]);
    }
}

測試新增圖的程式碼:

int main(){
    int N,T; //節點總數和邊數 節點ID從1開始
    cin>>N>>T;
    int s,e;
    for(int i = 0;i < T; i++){
        cin>>s>>e;
        graph[s].push_back(e);
    }
    for(int i = 1; i <= N; i++){
        if(dfn[i] == 0){
            tarjan(i);
        }
    }
    for(int i = 1; i <= N; i++){
        cout<<tpArr[i]<<' ';
    }
    return 0;
}

自己可以按照上面的圖輸入一下。

這個演算法可以解決2-sat問題

相關推薦

圖強連通分量的tarjan算法原理的一點理解

深度優先 含義 出現 組合 分支 ron 滿足 根節點 節點和 先簡單敘述一下tarjan算法的執行過程(其他諸如偽代碼之類的相關細節可以自己網上搜索,這裏就不重復貼出了): 用到兩類數組: dfs[]:DFS過程中給定節點的深度優先數,即該節點在DFS中被訪問的次序 lo

圖的強連通分支(Tarjan演算法)

強連通分支 如果兩個頂點可以相互通達,則稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components

『圖論』圖強連通分量的Tarjan演算法

在圖論中,一個有向圖被成為是強連通的(strongly connected)當且僅當每一對不相同結點u和v間既存在從u到v的路徑也存在從v到u的路徑。有向圖的極大強連通子圖(這裡指點數極大)被稱為強連通分量(strongly connected component)。 比如說這個有向圖中,點\(1,2,

圖強連通分量的Tarjan算法

雙向 強連通分量 地址 nbsp 指向 代碼 堆棧 全部 blank 原文地址:https://www.byvoid.com/blog/scc-tarjan/ [有向圖強連通分量] 在有向圖G中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly

筆記:Tarjan算法 求解圖強連通分量的線性時間的算法

true fff lan number lock 無環 還需 sin 第一次 Tarjan他爾賤算法 求解有向圖強連通分量的線性時間的算法 百度百科 https://baike.baidu.com/item/tarjan%E7%AE%97%E6%B3%95/10687825

夜深人靜寫演算法(十)- 圖強連通和2-sat問題

一、引例   1、同學會   【例題1】作者有N個同學,並且N個同學中有M對關係,M對關係描述為(a,b)代表a有b的電話號碼(不代表b有a的)。現在作者想舉辦一次同

圖強連通分量的Tarjian演算法

[有向圖強連通分量] 在有向圖G中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected

hdu1269 圖強連通 【Targan】(模板)

== color truct 相同 ext 結束 數據 訓練 算法 <題目鏈接> 題目大意: 為了訓練小希的方向感,Gardon建立了一座大城堡,裏面有N個房間(N<=10000)和M條通道(M<=100000),每個通道都是單向的,就是說若稱某通道

HDU3861-The King’s Problem(圖強連通縮點+最小路徑覆蓋)

題目連結 題意:題目大意:一個有向圖,讓你按規則劃分區域,要求劃分的區域數最少。  規則如下:  1、有邊u到v以及有邊v到u,則u,v必須劃分到同一個區域內。  2、一個區域內的兩點至少要有一方能到達另一方。  3、一個點只能劃分到一個區域內。 思路:根據規則1可

圖的強連通分量的算法

tin 存在 有向圖 pre sys nbsp 二維 ext 定義 下面是求有向圖的強連通分量的算法的代碼: import java.util.Scanner; class Qiufenliang//定義求強連通分量的類 { String lu="";//定義的一

圖的強連通分量(c語言版)

 有向圖G: 1.選用何種結構儲存有向圖? 選用十字連結串列的結構來儲存圖 2.選用何種遍歷方式? 選用深度優先搜尋 3.思路 3.1 首先從圖G上某個頂點出發沿以該頂點為尾的弧深度優先搜尋,在頂點深度優先搜尋結束之時把該頂點存放到輔助陣列finished中(程式中實際存

網中任意一對頂點之間的最短路徑 Floyd演算法

       有向網中欲知任意一對頂點之間的最短路徑常用Floyd演算法,基本思想是任意一對頂點vi與vj,逐步在其中加入一箇中間節點v0,..,vk,...vn,若比原路徑短則更新最短路徑,經過n次比較和修正後,vi到vj的最短路徑可以求出。具體程式碼及相關注釋如下 //

【ZSTU4213 2015年12月浙理工校賽 D】【雙連通分量tarjan演算法】One-Way Roads 無連通圖確定邊的方向使得全圖任意兩點間可達

4213: One-Way Roads Time Limit:1 Sec  Memory Limit:128 MB  Special JudgeSubmit:133  Solved:45 Description In the ACM kingdom, there a

LeetCode 210. Course Schedule II(拓撲排序-圖中是否存在環)

target inpu begin urn take before amp 存在 fin 和LeetCode 207. Course Schedule(拓撲排序-求有向圖中是否存在環)類似。 註意到。在for (auto p: prerequistites)中特判了

迷宮城堡//強連通分支Tarjan

ria include align 單向 names tput rip pan 兩個 題目: 迷宮城堡 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

圖___連通分量個數

求無向圖連通分量個數方法:    基於DFS,從某一頂點出發遍歷圖,for迴圈,改變起始頂點,count計數。 程式碼如下: void DFSTraverse(ALGraph G){ //深度遍歷圖 void DFS(ALGraph G,

C語言利用圖的鄰接表的儲存方式實現圖的入度和出度以及無圖的度數

Description 圖採用鄰接表為儲存結構,圖中的頂點數為n(0<n<=20),n個頂點的資訊依次為 0,1,...,n-1。 編寫程式,輸入圖的型別(0:無向圖,1:有向圖)、圖中頂點數、邊數、邊的偶對,建立圖的鄰接表。如果是無向圖,計算並輸出每個頂點的度;如果是有向圖,計

HDU1269 強連通分量-tarjan演算法

題意就是在給定一個圖的情況下,問這個單向圖是不是強連通圖。 強連通圖的意思就是圖中任何一個點都可以到達另一個點。 分析:這道題是tarjan演算法求強連通分量的模板題,對於tarjan演算法求連通分

[圖] 7.30 圖中所有簡單迴路-鄰接表-DFS

題目來源:嚴蔚敏《資料結構》C語言版本習題冊 7.30 【題目】試寫一個求有向圖G中所有簡單迴路的演算法 【測試資料】123456對應ABCDEF 【結果】 【答案】 /*-----------------------------------------

數算實習 popular cow 強連通分量tarjan演算法

popular cow 描述:有N頭牛。如果a喜歡b,b喜歡c,則a也會喜歡c。告訴你M個喜歡關係 ,比如(a,b)表示a喜歡b。問有多少頭牛是被所有牛都喜歡的。 N<= 10,000, M<= 50,000 樣例輸入 3 3 1 2 2 1 2