Kosaraju算法(强连通分量分解)

对于一个有向图顶点的子集S,如果在S内取两个顶点u和v,都能找到一条从u到v的路径,那么就称S是强连通的。如果在强连通的顶点集合S中加入其他任意顶点集合后,它都不再是强连通的,那么就称S是原图的一个强连通分量(SCC:strongly connected component)。任意有向图都可以分解成若不相干的强连通分量,这就是强连通分量分解。把分解后的强连通分量缩成一个顶点,就得到了一个DAG(有向无环图)。

Kosaraju算法(强连通分量分解)_第1张图片

强连通分量分解可以通过两次简单的DFS实现。第一次DFS时,选取任意顶点作为起点,遍历所有尚未访问过的顶点,并在回溯前给顶点标号(post order,后序遍历)。对剩余的未访问过的顶点,不断重复上述过程。

完成标号后,越接近图的尾部(搜索树的叶子),顶点的标号越小。第二次DFS时,先将所有边反向,然后以标号最大的顶点为起点进行DFS。这样DFS所遍历的顶点集合就构成了一个强连通分量。之后,只要还有尚未访问的顶点,就从中选取标号最大的顶点不断重复上述过程。

Kosaraju算法(强连通分量分解)_第2张图片

可以将强连通分量缩点并得到DAG。此时可以发现,标号最大的节点就属于DAG头部(搜索树的根)的强连通分量。因此,将边反向后,就不能沿边访问到这个强连通分量以外的顶点。而对于强连通分量内的其他顶点,其可达性不受边反向后的影响,因此在第二次DFS时,可以遍历一个强连通分量里的所有顶点。

 Kosaraju算法(强连通分量分解)_第3张图片

时间复杂度分析:只进行了两次DFS,因而总的时间复杂度时O(V+E)。

附上代码:

#include
#include
#include
#include
 
using namespace std;
 
const int max_v=100;
 
int V;
vectorg[max_v];
vectorrg[max_v];
vectorvs;
bool used[max_v];
int cmp[max_v];
 
void add_edge(int from,int to)
{
    g[from].push_back(to);
    rg[to].push_back(from);
}
 
void dfs(int v)
{
    used[v]=true;
    for(int i=0;i=0;i--){
        if(!used[vs[i]]){
            rdfs(vs[i],k++);
        }
    }
    return k;
}
 
int main()
{
    scanf("%d",&V);
    int m;
    scanf("%d",&m);
    int u,v;
    for(int i=0;i

 

你可能感兴趣的:(强连通分量)