转自scameeling的空间
http://hi.baidu.com/scameeling/item/b135831094ec756771d5e815
强连通分量 Kosaraju PK Tarjan
Kosaraju算法
对每个不在树中的点开始DFS一次,并记录离开各点的时间,这里是离开的时间,而不是到达时的,比如有图1->2 2->3 则1,2,3分别对应的时间是3 2 1,因为3没有出边,所以最先离开,其次是2,最后是1,
DFS后,在同一棵树中的点,如果dfn[v]>dfn[u]则说明点从v有可能到达u,而这棵树中的dfn[]最大的点,肯定可以到达每个点,从而在原图的逆图中,每次都选没有访问过的最大的dfn值开始DFS,如果可达点x 则说明它们是强连通的
void DFS_T(int u)
{
int i,v;
if(used[u])return ;
used[u]=1;id[u]=scc;
for(i=q[u];i!=-1;i=Tedge[i].pre)
{
v=Tedge[i].d;
if(!used[v])DFS_T(v);
}
}
void DFS(int v){
int i,u;
if(used[v])return ;
used[v]=1;
for(i=p[v];i!=-1;i=edge[i].pre)
{
u=edge[i].d;
if(!used[u])DFS(u);
}
order[++num]=v;
}
int Kosaraju()
{
int i,j,k,v,u;
memset(used,0,sizeof(used));num=0;
for(i=1;i<=n;++i)if(!used[i])DFS(i);
memset(used,0,sizeof(used));
memset(id,0,sizeof(id));scc=0;
for(i=num;i>=1;--i)if(!used[order[i]])scc++,DFS_T(order[i]);
}
Tarjan算法
dfn[v] 记录到达点v的时间,跟上面的离开不同,low[v]表示通过它的子结点可以到达的所有点中时间最小值,即 low[i]=min(low[i],low[u]),u为v的了孙,初始化时low[v]=dfn[u]。如果low[v]比dfn[v]小,说明v可 以通过它的子结点u,u1,u2...到达它的祖先v',则存在环,这个环上所有的点组成的子图便是一个强连通分量。换一个角度看,如果当 low[v]==dfn[v]时,则它的子树中所有low[u]==dfn[v]的点都与v构成一个环,维护一个栈,DFS过程中,每遍历一个点则把它放 入栈中,当发现low[v]==dfn[v]则依次把栈里的元素都弹出来,当栈顶元素为v时结束,这些点便构成一个以v为树根的强连通分量。
仍以上图为例,首先遍历点1,并dfn[1]=low[1]=++num, num表示按先后访问时间编号 ,同时1入栈
a.从3深入 dfn[3]=low[3]=2; 3入栈
b.从3到5 dfn[5]=low[5]=3; 5入栈
c.从5到6 dfn[6]=low[6]=4; 6入栈
d.发现6没有子结点可走,这时判断dfn[6]==low[6],于是开始弹栈,当遇到6时则break,即共弹出一个元素,于是6便是一个强连通分量
e.回溯至5,同样判断和弹栈,发现5也是一个强连通分量
f.再回溯至3,发现有边3->4,dfn[4]=low[4]=5,4入栈
g.4有边到1,由于1已经在栈里面,所以用dfn[1]更新low[4] 即low[4]=min(low[4],dfn[1])=1
h.回溯更新4的父亲3的low值 low[3]=min(low[3],low[4])=1
i.再回溯至1,发现有边1->2 继续深度遍历,2入栈,发现它的子结点4已经在栈中,直接更新low[2]=min(low[2],dfn[4]);
j.回溯至1,从而1所有出发的边都走了一遍,这时再比较low[1]与dfn[1],发现相等,于是开始弹栈,找到2,4,3,1这四个元素,构成一个连通分量。
void Tarjan(int v){
dfn[v]=low[v]=++num;
used[v]=1;
st[++numSt]=v;
for(int i=p[v];i!=-1;i=edge[i].pre){
int u(edge[i].d);
if(!dfn[u])//还没有标号的点
{
Tarjan(u);//先遍历它的子结点
GetMin(low[v],low[u]);//用子结点更新当前点的low值
}
else if(used[u]&&GetMin(low[v],dfn[u]));
}
if(dfn[v]==low[v]){
scc++;
while(1){
int u(st[numSt--]);
id[u]=scc;
used[u]=0;
if(v==u)break;
}
}
}
int main(){
for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i);
}