双连通分量-tarjan

点双连通分量:

在无向连通图中,如果删除该图的任何一个结点都不能改变该图的连通性,则该图为双连通的无向图。一个连通的无向图是双连通的,当且仅当它没有关键点.

强连通分量:

在有向图G中,如果两个顶点vi,vj间(vi<>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量。

tarjan算法:

tarjan(u)
{
    DFN[u]=Low[u]=++Index                      // 为节点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)
}
tarjan算法的本质:

对所有的节点进行dfs,并且在遍历的时候记录搜到这个点的时间,用dfn存储。当y搜索到的某个节点x之前搜索到了,那么

说明从节点x到节点y这一段路程组成一个强连通分量。具体代码实现就是每搜过一个点就把这个点放进栈里,low数组记录这个节

点以及这个节点所在的连通分量中的最小dfn,当low[x]=dfn[x]的时候,说明已经形成了一个连通分量,那么把栈里的节点出栈到x。

tarjan无向图求点双联通:

void tarjan(int x)
{
    int i;
    low[x]=dnf[x]=times++;
    for(i=head[x];i!=-1;i=edge[i].next)
    {
        if(vis[i])continue;
        vis[i]=vis[i^1]=1;
        int y=edge[i].e;
        if(!dnf[y])
        {
            st.push(i);
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dnf[x])//这个时候,栈里的点构成一个双连通分量
            {
                while(1)
                {
                    yw=st.top();
                    st.pop();
                    if(edge[yw].u==x)break;
                }
            }
        }
        else if(dnf[y]<dnf[x])
        {
            st.push(i);
            low[x]=min(low[x],dnf[y]);
        }
    }
}

tarjan有向图求强联通分量:

void tarjan(int x)
{
    dnf[x]=low[x]=times++;
    instack[x]=1;
    qq.push(x);
    for(int i=head[x];i!=-1;i=edge[i].next)
    {
        int y=edge[i].v;
        if(!dnf[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(instack[y])
        {
            low[x]=min(low[x],dnf[y]);
        }
    }
    if(low[x]==dnf[x])
    {
        int y=-1;
        while(x!=y)
        {
            y=qq.top();
            qq.pop();
            instack[y]=0;
        }
        nums++;
    }
}

tarjan算法求完缩点之后,对于有向图:

如果入度为0的点的个数为a,出度为0的点的个数为b;

那么:最少需要加max(a,b)条边使得原图变成强联通图。


你可能感兴趣的:(双连通分量-tarjan)