本来我是打算去网上看Tarjan求LCA的。。结果发现了一篇挺好的介绍tarjan算法求强连通分量的文章。
首先理解什么是强连通分量,简单来说,可以相互连通的区域成为强连通分量;若是u能到达v且v能到达u,且u,v与其它结点不能相互连通,则{u,v}是一个强连通分量。
谈谈我对tarjan的理解,图解过程详见:http://www.cnblogs.com/shadowland/p/5872257.html,解释得真的很好。
tarjan基于深搜,首先定义一个栈用于存储结点,在深搜的过程中,结点进栈,DFN[i]表示第i结点是第几个被遍历的(或者说是第几个进栈的);
LOW[i]表示第i结点及i所在的子树可追溯到的最早进栈的结点的进栈次序,若拓展结点时找到了在栈中的结点,则更新LOW取较小的值;
回溯时,若DFN[i]==LOW[i],则i结点到栈顶结点所有结点为同一强连通分量。
详见代码:
void tarjan(int now) //用链式前向星存图实现的tarjan
{
dfn[now]=++t;
low[now]=t;
flag[now]=true; //flag表示是否被访问过
stack[++top]=now;
int k=top;
int u=head[now];
while (u!=0)
{
if (!flag[edge[u].to]) tarjan(edge[u].to);
if (!del[edge[u].to]) low[now]=min(low[now],low[edge[u].to]); //del表示是否在栈中,为false,说明edge[u].to未被归到强连通分量里,那么这两个点必定互相连通(可画图证明)
u=edge[u].next;
}
if (dfn[now]==low[now]) //满足该条件,则当前点一直到栈顶点已经构成一个强连通分量
{
for (int i=k;i<=top;i++) del[i]=true; //出栈
if (top-k+1>ans) //此程序为取最大结点数最小字典序的强连通分量
{
ans=top-k+1;
for (int i=k;i<=top;i++) que[i-k+1]=stack[i];
sort(que+1,que+top-k+1+1,cmp); //stl库排序,终止点为数列长度+1
}
else if (top-k+1==ans)
{
for (int i=k;i<=top;i++) comparison[i-k+1]=stack[i];
sort(comparison+1,comparison+top-k+1+1,cmp);
if (comparison[1]<que[1])
for (int i=1;i<=ans;i++)
que[i]=comparison[i];
}
top=k-1; //出栈
}
}