无向图的割顶,桥和构造双连通分量以及有向图的极大连通分量,相关概念看这里 http://www.byvoid.com/blog/biconnect/
程序部分自己写,理论部分参考上方链接。
根据黑书上面的相关内容,无向图的tarjan算法大致如下:
/*
* 注释1:去除注释1即为无向图求割顶的算法。
* 特别的,若最终 sons>1,则节点1也为一个割点。
* 调用时 tarjan(1,0);
* 注释2:去除注释2即为无向图求桥的算法。
* bridge[i]为真表示边的邻接表中的第i条边为桥。
* 调用时 tarjan(1,0);
* low[]的编号即为连通分量的编号。
* */
void tarjan(int x,int father)
{
int i,j;
dfn[x] = low[x] = ++index;
for(i=head[x];i;i=edge[i].next)
{
j = edge[i].v;
if(j!=father) //这个判断不要忽略了,特别在无向图中很重要。
{
if(!dfn[j])
{
//1 if(x==1) sons++;
tarjan(j,x);
low[x] = min(low[x],low[j]);
//1 if(x!=1 && low[j]>=dfn[x]) cut[x] = true;
//2 if(low[j]>dfn[x]) bridge[i] = true;
}else
low[x] = min(low[x],dfn[j]);
}
}
return;
}
若要求构造双连通分量,则要利用LCA算法,还是Tarjan的成果。参见上方链接。
有向图的tarjan算法,求极大连通分量
/*
* 有向图的tarjan算法,求极大连通分量。
* 与无向图相比,去掉 j!=father 的判断。
* 另外,low[]的编号不能表示连通分量的编号,
* 而必须用栈来对结点进行编号。
* 调用时,由于有多个连通分量,一次dfs无法全部遍历,
* 所以:
* for(i=1;i<=n;i++)
* if(!dfn[i])
* tarjan(i);
* */
void tarjan(int x)
{
int i,j;
dfn[x] = low[x] = ++index;
for(i=head[x];i;i=edge[i].next)
{
j = edge[i].v;
//if(j!=father)
//{
if(!dfn[j])
{
tarjan(j,x);
low[x] = min(low[x],low[j]);
}else
low[x] = min(low[x],dfn[j]);
//}
}
if(low[x]==dfn[x])
{
label++;
do
{
j = stack[deep--];
lab[j] = label;
}while(j!=x);
}
return;
}