标签:
强连通分量
强连通分量(strongly connected component)是图论中的概念。图论中,强连通图指每一个顶点皆可以经由该图上的边抵达其他的每一个点的有向图。意即对于此图上每一个点对(Va,Vb),皆存在路径Va→Vb以及Vb→Va。强连通分量则是指一张有向图G的极大强连通子图G‘。如果将每一个强连通分量缩成一个点,则原图G将会变成一张有向无环图。一张图被称为有向无环图当且仅当此图不具有点集合数量大于一的强连通分量,因为有向环即是一个强连通分量,而且任何的强连通分量皆具有至少一个有向环。

比如说这个有向图中,点 1,2,4,5,6,7,8 和相应边组成的子图就是一个强连通分量,另外点3,9 单独构成强连通分量
Tarjan 算法是由 Robert Tarjan 提出的用于寻找有向图的强连通分量的算法。它可以在O(|V|+|E|)的时间内得出结果。(运行Tarjan算法的过程中可以知道,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(|V|+|E|))
Tarjan 算法主要是利用 DFS 来寻找强连通分量的。在介绍该算法之前,我们先来介绍一下搜索树。先前那个有向图的搜索树是这样的:

有向图的搜索树主要有 4 种边(这张图只有 3 种),其中用实线画出来的是树边(tree edge),每次搜索找到一个还没有访问过的结点的时候就形成了一条树边。用长虚线画出来的是反祖边(back edge),也被叫做回边。用短虚线画出来的是横叉边(cross edge),它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点并不是当前节点的祖先时形成的。除此之外,像从结点1到结点6这样的边叫做前向边(forward edge),它是在搜索的时候遇到子树中的结点的时候形成的,容易知道移除前向边不会改变图的连通性,所以在讨论中基本忽略它。另外,还有从一棵搜索树上的节点到另一棵搜索树上的节点的边,称为跨树边,但是跨树边在这里也发挥不了大的作用。
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
Tarjan 算法主要是在 DFS 的过程中维护了一些信息:dfn、low 和一个栈。
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算法的操作原理如下:
对于情况(2),<i, j>是逆向边,显然i、j处于同一个强连通分支;
对于情况(3):<i, j>是横叉边。显然i、j必然在同一棵搜索树中(因为搜索树的根结点肯定满足low=dfn),设p=LCA(i, j),由于从p到j的路径上木有low=dfn的结点(否则j已经出栈了),所以j必然可以到达p,又因为p可以到达i,所以j也可以到达i,又因为存在边<i, j>,所以i、j处于同一个强连通分支,这样就需要在计算low[i]的时候把dfn[j]考虑进去,而不能让i及其所有后代成为一个强连通分支。
/* 寻找有向图强连通分量的tarjan算法
* index表示的就是时间戳
* scc_cnt 表示强连通分量的个数
* belong[u] 表示结点u属于那一个强连通分量
* inst[u] 表示结点u是否仍然在栈中
* st[] 和 top 分辨表示栈和栈顶位置
*index = scc_cnt = top = 0*/
void targin(int u)
{
int v;
dfn[u] = low[u] = ++index;
st[++top] = u;
inst[u] = 1;
for (int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].v;
if (!dfn[v])
{
targin(v);
low[u] = min(low[u],low[v]);
}
else if (st[v])
low[u] = min(low[u],dfn[v]);
}
if (low[u] == dfn[u])
{
scc_cnt++;
do
{
v = st[top--];
inst[v] = 0;
belong[v] = scc_cnt;
}
while (u != v);
}
}
标签:
原文地址:http://www.cnblogs.com/zzy19961112/p/5860305.html