zoukankan      html  css  js  c++  java
  • tarjan算法

    先再明确几个概念:
    强连通图:强连通图。在一个强连通图中,任意两个点都通过一定路径互相连通。比如图一是一个强连通图,而图二不是。因为没有一条路使得点4到达点1、2或3。
    这里写图片描述
    强连通分量:在一个非强连通图中极大的强连通子图就是该图的强连通分量。比如图三中子图{1,2,3,5}是一个强连通分量,子图{4}是一个强连通分量。
    这里写图片描述 (图片来自其他博客)
    tarjan算法的实现:
    tar tarjan基于DFS,用dfs遍历图G的所有点。分别建立两个数组Dfn和Low,Dfn[i]代表dfs到达顶点i的时间,Low[i]代表能够直接或者间接到达的最小顶点时间。
    1.初始化,当首次搜索到点i时,Dfn与Low数组的值都为到该点的时间,Low[i]=Dfn[i]=time++。
    2.将搜索到的点压入栈顶。
    3.当点u与点v相连,如果v并不在栈中,先将v压入栈,dfs遍历v,则Low[u]=min(Low[u],Low[v]).
    4.当点u与点v相连,如果v在栈中,则Low[u]=min(low[u],dfn[v]).
    5.每当搜索到的点经过以上操作后,Low[u]=Dfn[u],则此时栈里u以及u以上的顶点全部出栈,即为一个极大强连通分量。
    6.继续搜索,或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。
    伪代码:

    tarjan(u)
    {
        DFN[u]=Low[u]=time++                // 为节点u设定次序编号和Low初值
        Stack.push(u)                       // 将节点u压入栈中
        for each (u, v) in E                // 枚举每一条边
            if (v is not visted)            // 如果节点v未被访问过
                tarjan(v)                   // 继续向下找
                Low[u] = min(Low[u], Low[v])        
            else if (v in S)                // 如果节点v还在栈内
                Low[u] = min(Low[u], DFN[v])    
            if (DFN[u] == Low[u])           // 如果节点u是强连通分量的根
                 repeat
                     v = S.pop              // 将v退栈,为该强连通分量中一个顶点
                     print v
                 until (u== v)
    }

    时间复杂度:
    所有的点都刚好进过一次栈,所有的边都访问的过一次,所以时间复杂度为O(n+m)。
    证明:
    1.在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点: 因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。
    2.dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于 自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。
    代码实现:

    int taijan(int i)
    {
        int j;
        Dfn[i]=Low[i]=Dindex++;
        instack[i]=true;       //标记已经访问
        stap[num++]=i;         //记录进栈的顺序,值
        for(edge e=V[i];e;e=e->next)
        {
            j=e->t;
            if(!Dfn[j])
            {
                tarjan(j);
                if(Low[j]<Low[i])
                Low[i]=Low[j];
            }
            else if(instack[j]&&Dfn[j]<Low[i])
            Low[i]=Dfn[j];
        } 
        if(Dfn[i]==Low[i])
        {
            Bcnt++;             //记录连通块个数
            do
            {
                j=stap[num--];
                instack[j]=false;
                belong[j]=Bcnt;  //记录各点属于哪一个连通块
            }while(j!=i)
        }
    }
    int solve()
    {
        int i;
        num=Bcnt=Dindx=0;
        memset(Dfn,0,sizeof(Dfn));
        for(i=1;i<=N;i++)
        {
            if(!Dfn[i])
            tarjan(j);
        }
    } 

    参考文献:
    http://my.oschina.net/u/572632/blog/277951
    http://blog.csdn.net/xinghongduo/article/details/6195337
    http://blog.sina.com.cn/s/blog_69a101130100k1cm.html

  • 相关阅读:
    python基础之函数
    MySQL 安装 5.0
    MySQL安装 MySQL5.7.10免安装版配置,mysql5.7.10免安装版
    安装MYSql Windows7下MySQL5.5.20免安装版的配置
    网站集合
    andorid 开放工具集合
    Win7 x64 Eclipse无法识别手机 / adb interface有黄色感叹号,无法识别
    字符集 ISO-8859-1(3)
    字符集 ISO-8859-1(2)
    字符集 ISO-8859-1(1)
  • 原文地址:https://www.cnblogs.com/luckycode/p/5255657.html
Copyright © 2011-2022 走看看