强连通图:任意两点可以互达的有向图。
强连通分量:极大强连通子图。
白色路径定理:请自行百度(不是重点)
1.首先从一般的算法开始
一个有向图可以看成连通分量组成的有向无环图(但要注意,连通分量之间可能有多条边);
否则,不同连通分量将被连通。
可以看到,只要按逆拓扑序依次遍历连通分量,可以依次得到连通分量。
我们知道,利用深度优先搜索,按退出dfs的顺序可以得到有向无环图的逆拓扑序,但两者毕竟还是有区别的,很不凑巧,在连通分量组成的有向无环图中,最先退出dfs的可能不是出度为0的连通分量(可以先在自家绕一圈嘛)。
但是,最后退出dfs的顺序确实是逆拓扑序。
严格证明需要用到白色路径定理(听起来高大上,其实很low,但真的很有用)
所以简单说明一下,假设先进行后继分量的搜索,那么等搜索完了也搜索不到前驱。
假设先进行前驱搜索(此时后继要么搜完了,要么没动,不可能正在搜),那么后继没搜完,前驱就不会退出。
因此,前驱比后继退出dfs要晚。
但是,但是,是最晚退出dfs的时间要晚,也就是说,对于所有顶点,只能保证最晚退出的是拓扑序的第一个,而不能保证最早退出的是逆拓扑序的最后一个。我们只能以拓扑序的顺序得到连通分量,这要求我们使用转置图(把所有边方向反转,连通分量不变),这样就变成转置图的逆拓扑序;
好了,思想相信你已经get了。具体做法就是,先dfs得到顶点退出时间,再从最晚顶点开始不断在转置图上“摘”连通量。
2.tarjan算法
结合深度优先搜索图看一下
根据白色路径定理,分量上第一被发现的点,是同分量其他点的祖先。
但不排除后代没有其他连通分量。
所以我们要以一定顺序,把连通分量摘下来,使得每次连通分量不包含其他连通分量构成的子树。
根据前面的讨论,最晚节点退出dfs的顺序是一个逆拓扑序(即根节点/祖先退出的顺序),可以按这个顺序摘。
但现在我们从另一个角度来看,如果根结点退出dfs,那么子树也早就退出dfs,所以如果一个连通分量根节点不包含其他连通分量构成的子树,那么他最先退出dfs,因此我们可以通过退出dfs的顺序摘分量。
现在只要判断退出dfs的是不是根节点了。
开动开动脑筋吧,下一个图灵奖得主。
好吧,你的脑子说你不想。
注意,在一棵连通分量子树中,对于一个节点(除根节点),自己或者其后代必然到祖先有直接的边,否则就不能和祖先互达(这里没有说根节点是因为可能仅到祖先有直接的边,再通过祖先与根节点连通)
若我们设置一个访问顺序数组dfn,和一个存储访问到的(不是有路可达的,见反例)最早点数组low。
若自己或者其后代必然到祖先有直接的边,再通过回溯,那么最早点数组里就是某个祖先的访问时间(根节点除外)。
这样,若low[i]==dfn[i],就能断定该点是根节点(你可能要问,根节点到其他分量有边怎么办,结合第一个图,分量只能到后继有边,后继在其根节点退出dfs已被摘除,根本无法回溯)。
具体做法,用栈存储搜索树,dfs的时候判断根节点,如果是的话,把树摘出来。
代码的话,去网上搜吧。
相关信息见《算法导论》第三版p357