tarjan algorithm

form:Christopher Yanios

概念

若是兩個頂點能夠相互通達,則稱兩個頂點強連通算法

若是有向圖G的每兩個頂點都強連通,稱G是一個強連通圖.數組

非強連通圖有向圖的極大強連通子圖,稱爲強連通份量數據結構

\(Tarjan\)算法是用來求強連通份量的,它是一種基於\(DFS\)(深度優先搜索)的算法,每一個強連通份量爲搜索樹中的一棵子樹。而且運用了數據結構棧。spa

在介紹詳細原理前,先引入兩個很是重要的數組:\(dfn[ ]\)\(low[ ]\)code

\(dfn[ ]\):就是一個時間戳(被搜到的次序),一旦某個點被\(DFS\)到後,這個時間戳就再也不改變(且每一個點只有惟一的時間戳)。因此常根據\(dfn\)的值來判斷是否須要進行進一步的深搜。orm

\(low[ ]\):該子樹中,且仍在棧中的最小時間戳,像是確立了一個關係,\(low[ ]\)相等的點在同一強連通份量中。blog

注意初始化時 \(dfn[ ] = low[ ] = ++cnt.\)get

算法思路:

首先這個圖不必定是一個連通圖,因此跑\(Tarjan\)時要枚舉每一個點,若\(dfn[ ] == 0\),進行深搜。string

而後對於搜到的點尋找與其有邊相連的點,判斷這些點是否已經被搜索過,若沒有,則進行搜索。若該點已經入棧,說明造成了環,則更新low.

在不斷深搜的過程當中若是沒有路可走了(出邊遍歷完了),那麼就進行回溯,回溯時不斷比較\(low[ ]\),去最小的low值。若是\(dfn[x]==low[x]\)\(x\)能夠看做是某一強連通份量子樹的根,也說明找到了一個強連通份量,而後對棧進行彈出操做,直到x被彈出

手動模擬一下過程:

從1進入 dfn[1]= low[1]= ++cnt = 1
入棧 1
由1進入2 dfn[2]=low[2]= ++cnt = 2
入棧 1 2
以後由2進入4 dfn[4]=low[4]= ++cnt = 3
入棧 1 2 4
以後由4進入 6 dfn[6]=low[6]=++cnt = 4
入棧 1 2 4 6

6無出度,以後判斷 dfn[6]==low[6]
說明6是個強連通份量的根節點:6及6之後的點出棧並輸出。

回溯到4後發現4找到了一個已經在棧中的點1,更新 low [ 4 ] = min ( low [ 4 ] , dfn [ 1 ] )

因而 low [ 4 ] = 1 .

由4繼續回到2 Low[2] = min ( low [ 2 ] , low [ 4 ] ).
low[2]=1;
由2繼續回到1 判斷 low[1] = min ( low [ 1 ] , low [ 2 ] ).
low[1]仍是 1
而後更新3的過程省略,你們能夠本身手動模擬一下。

。。。。。。。。。

省略了1->3的更新過程以後,1的全部出邊就跑完了

因而判斷:low [ 1 ] == dfn [ 1 ] 說明以1爲根節點的強連通份量已經找完了。

將棧中1以及1以後進棧的全部點,都出棧並輸出

#include<iostream>  //輸出全部強連通份量
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
	int x = 0, f = 1;char ch = getchar();
	while (ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
	return x * f;
}
int n,m,x,y,top=0,cnt=0,t,col;
int ans1=-1,ans2=-1,ans3=-1;
int d[200020];
int a[200020];
int c[200020];
int f[200020];
int dfn[200020];
int low[200020];
int stack[200020];

bool v[200020];

struct edge{
    int u;
    int v;
    int w;
    int next;
}e[1000020];

void Add(int u,int v,int w){
    e[++top].v=v;
    e[top].w=w;
    e[top].next=f[u];
    f[u]=top;
}

void tarjan(int now)
{
    dfn[now]=low[now]=++cnt;
    stack[++t]=now;
    v[now]=1;
    for(int i=f[now];i!=-1;i=e[i].next)
        if(!dfn[e[i].v]) {
            tarjan(e[i].v);
            low[now]=min(low[now],low[e[i].v]);
        }
        else if(v[e[i].v])
            low[now]=min(low[now],dfn[e[i].v]);    
    int cur;
    if(dfn[now]==low[now]){
        do
        {
            cur=stack[t--];
            v[cur]=false;
            printf("%d ",cur);
        }while(now!=cur);
        printf("\n");
    }
}

int main()
{
    n=read();
    m=read();
    memset(f,-1,sizeof f);
    for(int i=1;i<=n;++i)
        a[i]=read();
    for(int i=1;i<=m;++i)
    {
        x=read();
        y=read();
        Add(x,y,0);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i]) tarjan(i);
    return 0;
}
相關文章
相關標籤/搜索