强连通分量的Tarjan算法

有向图的强连通分量(SCC)算法

强联通分量定义

给定有向图 G ( V , E ) G(V,E) G(V,E)

u , v ∈ V u,v\in V u,vV,我们定义函数 J [ u ] [ v ] J[u][v] J[u][v],若 u u u可以到达 v v v,则有 J [ u ] [ v ] = 1 J[u][v]=1 J[u][v]=1,否则 J [ u ] [ v ] = 0 J[u][v]=0 J[u][v]=0

对于非空点集 C i ⊂ V C_i\subset V CiV,且 ∀ u , v ∈ C i , J [ u ] [ v ] = J [ v ] [ u ] = 1 \forall u,v\in C_i,J[u][v]=J[v][u]=1 u,vCi,J[u][v]=J[v][u]=1, ∀ u ∉ C i , 有 ∀ v ∈ C i J [ u ] [ v ] + J [ v ] [ u ] < 2 \forall u\notin C_i,有\forall v\in C_i J[u][v]+J[v][u]<2 u/Ci,vCiJ[u][v]+J[v][u]<2,称 C i C_i Ci为图的一个强联通分量SCC;

对于图的所有强联通分量 C 1 , C 2 , C 3 . . . . , C k C_1,C_2,C_3....,C_k C1,C2,C3....,Ck,我们可以将每一个强联通分量视为一个节点,若 ∃ u , v , u ∈ C i , v ∈ C j , i ≠ j \exist u,v,u\in C_i,v\in C_j,i\neq j u,v,uCi,vCj,i=j有$ J[u][v]=1 , 则 我 们 对 ,则我们对 ,C_i,C_j 建 立 一 条 单 向 边 , 即 记 建立一条单向边,即记 J[C_i][C_j]=1 ; 我 们 记 新 形 成 的 图 为 ;我们记新形成的图为 ;G^{’}(V,E)$,根据强联通分量的定义,新图是有向无环图;

T a r j a n Tarjan Tarjan算法

我们可以利用新建的 G ′ ( V , E ) G'(V,E) G(V,E)是有向无环图的性质,通过先访问出度为0的节点,来实现 S C C SCC SCC的查询。最直接的做法是先反向建图,然后进行深搜,计算节点的完成时间,按照节点完成时间的升序,在原图上进行遍历,每次遍历到的连通块便是一个强连通分量。但是这样便需要对图进行两次遍历,于是我们可以还是通过建立新图的思想来实现 S C C SCC SCC的查找,我们利用深度优先搜索的特性,对于任意一个强连通分量而言,必然存在一个时刻强联通分量的点都在栈内,并且栈内元素都可以遍历到第一个入栈的点,所以我们对于每个节点可以记录一个low值来表示该节点能够到遍历到的最早进入栈中的元素的入栈时间戳是多少。那么,我们可以将最早入栈的元素视为这个强连通分量的代表元素,即最早入栈元素完成搜索后,其自身到栈顶便是一个强连通分量。

Tarjan(int s){
    use[s]=1;dfn[s]=low[s]=++tim;sta.push(s);
    for(int i=head[i];i;i=edge[i].pre){
        int to=edge[i].to;
        if(!use[s]&&dfn[s]) continue;
        if(!dfn[s]) Tarjan(to);
        low[s]=min(low[s],low[to]);
    }
    if(low[s]!=dfn[s]) return;
    ++cl;
    int now=sta.top();
    while(low[now]!=dfn[now]){
        use[now]=0;
        col[now]=cl;
        sta.pop();
        now=sta.top();
    }
    use[now]=0;
    col[now]=cl;
    sta.pop();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值