zoukankan      html  css  js  c++  java
  • tarjan algorithm

    form:Christopher Yan

    概念

    如果两个顶点可以相互通达,则称两个顶点强连通

    如果有向图 (G) 的每两个顶点都强连通,称 (G) 是一个强连通图.

    非强连通图有向图的极大强连通子图,称为强连通分量

    (Tarjan) 算法是用来求强连通分量的,它是一种基于(DFS)(深度优先搜索)的算法,每个强连通分量为搜索树中的一棵子树。并且运用了数据结构栈。

    在介绍详细原理前,先引入两个非常重要的数组:(dfn[ ])(low[ ])

    (dfn[ ]):就是一个时间戳(被搜到的次序),一旦某个点被 (DFS) 到后,这个时间戳就不再改变(且每个点只有唯一的时间戳)。所以常根据 (dfn) 的值来判断是否需要进行进一步的深搜

    (low[ ]):该子树中,且仍在栈中的最小时间戳,像是确立了一个关系,(low[ ]) 相等的点在同一强连通分量中。

    注意初始化时 (dfn[ ] = low[ ] = ++cnt.)

    算法思路:

    首先这个图不一定是一个连通图,所以跑 (Tarjan) 时要枚举每个点,若 (dfn[ ] == 0),进行深搜。

    然后对于搜到的点寻找与其有边相连的点,判断这些点是否已经被搜索过,若没有,则进行搜索。若该点已经入栈,说明形成了环,则更新 (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("
    ");
        }
    }
    
    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;
    }
    
  • 相关阅读:
    这个命令可以看到你的cpu到底集合
    关于redis的主从、哨兵、集群(转)
    redis配置主从备份以及主备切换方案配置(转)
    redis主从配置及其java的调用(转)
    mongodb批量处理
    Java mongodb api疑问之MongoCollection与DBCollection
    JDK8日期处理API(转)
    Badboy + JMeter性能测试(转)
    Jmeter接口测试+压力测试(转)
    JMeter性能测试,完整入门篇(转)
  • 原文地址:https://www.cnblogs.com/Arielzz/p/14290078.html
Copyright © 2011-2022 走看看