这次我们来讲讲强连通分量 与 边双连通分量。
首先什么是强连通分量?
强连通分量指的是,在“有向图”中任意两个点能相互达到的联通块。
例如
这为一个强连通分量 这不是一个强连通分量
那如何去求出这样的分量呢。这就涉及到了tarjan算法。
Tarjan:
本质上就是DFS
需要在dfs的时候记录2个数组:dfn[],low[]
dfn[x]代表x点dfs到的时间,即时间戳,可知在同一个dfs树的子树中,dfn[x]越小,则其越浅。
low[x]代表在dfs树中,此点以及其后代指出去的边,能返回到的最浅的点的时间戳。
例如这张图
其缩点为
详细的过程例如以下的图
代码如下
对于强连通分量,经常还需要缩点建图,上述代码中,我们有个scc[]数组,表示x这个点所属的scc的编号。
#include<stdio.h>
#include<string.h>
#include<iostream>
#define N 105
using namespace std;
int n;
struct edge// 用链式前向星存储
{
int v,next;
edge(){}
edge(int v,int next)
{
this->v=v;
this->next=next;
}
}ed[N*N];
int head[N],lnum;
void addline(int u,int v)//添加新边
{
ed[lnum]=edge(v,head[u]);
head[u]=lnum++;
}
int index=0;
int dfn[N],low[N],sta[N];//dfn用来标记dfs序时间戳 low标记该点能达到的最小的时间戳的点
int scc[N]; //scc用来存储点所属强连通分量的标号
int top=0,sccnum=0;
void init()
{
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(head,-1,sizeof(head));
sccnum=index=top=lnum=0;
}
void tarjan(int root)
{
dfn[root]=low[root]=++index;
sta[top++]=root;
for(int i=head[root];~i;i=ed[i].next)
{
int y=ed[i].v;
if(!dfn[y])
{
tarjan(y);
low[root]=min(low[root],low[y]);
}
else if(!scc[y])
{
low[root]=min(low[root],dfn[y]);
}
}
if(low[root]==dfn[root])
{
int x;
sccnum++;
do
{
x=sta[--top];
scc[x]=sccnum;
}while(x!=root);
}
}
那么建图的话,只需要遍历原有的边,如果边所对应的两个节点所属的scc编号不同,那么则添边,最后得到一个有向无环图。