联通:无向图中从任意点i可到达任意点j;
强联通:在有向图中可以从任意点i到达任一点j;
弱联通:就是联通的意思啦:
联通分量的概念:
如图,对于这么一张有向图来说,(a,g,f)和(b,c,d)和e三组分别构成3个强联通分量。该算法的用途就是给你一个图让你求(a,g,f)和(b,c,d)和e这三组玩意
算法实现方面:基础算法是用dfs,dfs的遍历方式是先递归相邻节点,再访问当前节点。
比如上面那张图,;来到a这个点,我们先遍历b,f点后再来处理a,
这是基本递归结构
为了解该结构,我们再举个例子:
比如这个有向图,其递归过程为:
从上到下是时间顺序,比如dfs(a)了,对于a的处理就是先dfs(b)然后再来处理a这个点,以此类推递归下去。
前面那是个比较简单的例子,现在我们回到一开始给的图,那个复杂一些的有向图:
其递归过程就是:
然后我们给每个点两个参数(i,j),
i表示:dfs中该点被访问的时间点,比如a第一个被访问,a的i就是1,b第二个被访问,b的i就是2.
j表示:该节点通过有向边可以回溯到的最早时间点。比如a这个点。没有可以回朔的,就记为它本身,a的j等于1,同理b的j等于2,而g这个点,可以回到a这个点,而a的时间点为1,所以g这个点的j就是1;d这个点的j就等于b这个点的i,也就是2。
知道了这个之后,我们开一个栈,把每次遍历的节点压入栈内,(流程图中dfs(a)为遍历,最下面的a为对该点的处理)遍历一个点的时候入栈,处理这个点的时候出栈,比如根据上面的流程图,先运行5个dfs后,栈内变成(a,b,c,d,e),然后下面对e处理,处理得出e这个点的j是多少,发现e的j等于他自己的i,所以e出栈了,他就是单独一组,然后轮到处理d这个节点,发现d这个点是可以联通去b点的,所以d这个点的j就是b点的i,就是2。看c这个点,发现c这个点是可以去到d点的,而d点又能连到b点,所以c点的j就等于是d点的j,也是2,然后遍历到b点,发现b点的i和j是一样的,都是2,所以这3个点出栈,把这些j全是2的分为一组,也就是(b,c,d)是强连通的,另一组(a,f,g)同理。
代码思路:
开变量time:表示时间节点,即记录当前状态是运行到什么时间了。
开stack:栈
开变量v:表示全图中节点的个数
开dfn数组:dfn【n】=0,全初始化为0,表示上面提到的参数i。
开low【n】=0,初始化为0,表示上面提到的参数j噢。
主函数:
main(){
for(int x=0;x<=v;x++){
if(dfn[x]==0)dfs(x);//这里如果dfn为0的话就说明这个点没有被遍历过,这个循环用来解决那种两个图断开不连通的情况,保证所有点都处理到。
}
}
dfs函数:
dfs(x){
第1步:把x推入栈中。
第二步,给dfn和low赋值,low初始化等于dfn的值,到时候再该就好。
即:dfn【x】=low【x】=time;
第三步,遍历该节点能去的点(可用前向星等各种方式存图,我这里就简单一点拿二维存了,开数组g【x】【y】,如果x点到y点有联通,其值为1,否则为0)
for(y=0,y<v,y++){
if(g[x][y])//如果x到y有联通
if(dfn[y]==0)如果这个点没去过,就是说是往后走不是往前回朔的点的话
dfs(y)那就遍历他。
low【x】=min(low【x】,low【y】)。找x能去的i最小的点。
else {
· 要不然,如果y这个点是去过的点,判断一下他是不是在栈里面,在的话说明他们至少是联通的,栈里面的点我们假设如果是一条直线的关系:a->b->c->d那我们就只要找一条边能回朔的话就能框一段起来变成强联通,如
这个图,所以这里要处理的也是low【x】=min(low【x】,low【y】)。
}
}
最后处理一套下来之后如果dfn【x】==low【x】说明这个点没办法回朔到更早的点了,说明他要么不形成一个环要么就是一个环的最早那个点了已经发现一个环了,而不论是那种可能性,他都构成一个强联通(不形成环就是他本身1个为一组,形成环就是好几个点为一组)出栈。分组方面的话,看j相同的几个节点分为一组。(当一个点要出栈的时候,应为栈的结构特殊,这个点如果构成一个环的话,他的上面是有很多节点的,这些节点都是在他之后遍历入栈的,所以这些节点都是跟他为一个组的,我们只要一直出栈直到找到这个要出的点出去就行了)。
}