求有向图的强联通分量以及tarjan算法学习小结

什么是强联通分量

在一个有向图中:

1.如果两个点可以通过有向边互相联通,那么称这两个点强联通

2.如果图中任意两个点都强联通,那么称这个有向图为强连通图(一个点也是强连通图);

3.一个非强联通图中的极大强联通子图(不被另一个更大的强联通子图包含),称为这个图的强联通分量

如图:

图中的强连通分量有:{1,2,4}和{3}。

从中可以看出强连通分量有几个明显特征:

1.各个强连通分量中不会有重复的点。

2.一个强连通分量中,从任意一个点u都能通过有向边经过所有点,最后回到点u。

什么是tarjan

从上面的结论中可知,强连通分量就是极大有向环。

所谓tarjan,肯定是和DFS相关(不要问我为什么)。假设从u点DFS起,如果u在一个强连通分量内,那么最后肯定会回到u点。

tarjan算法的具体实现就是:

1.用dfn[u]记录u的搜索编号(表示u是第几个被搜到的点);

2.用low[u]表示从u往下搜可以搜到的搜索编号最小的点,初始化low[u]为dfn[u];

3.把u放进栈中,站内的点表示还未归为强连通分量内的点;

4.枚举被u连接的点往下搜,设这个点为v,如果v在栈中,即v已被搜过且未归为强连通分量内,则low[u]=min(low[u],dfn[v]);如果v没被搜过,则v肯定未归为强连通分量内,那么就DFS[v],并记录low[u]=min(low[u],low[v]);

5.最后,如果dfn[u]等于low[u]了,说明从u往下搜又会回到u(或者从u无法往下搜),由于在栈内,从u往下搜到的点都会堆在u上面,所以把栈内堆在u上的点和u一起出栈,并归为一个强连通分量。

在所有u搜到的点中,无法通过有向边回到u的点(无法与u强联通),要么连接到了一个dfn值比u大的点v,形成了一个小环,然后被出栈;

要么没有形成环,low值保持原来的与dfn值相等,也会被出栈(这就是一个点的强联通分量),

所以栈内剩下的堆在u上的点都是可以与u强联通的点。

比如上面那个图:

从点1搜起:

从2往下搜到4:

4连接到1,发现1在栈内,low改变,并回溯到2:

接下来,从2往3搜:

3无法再搜,dfn=low,出栈:

最后回到1,发现dfn=low,1、2、4一并出栈:

求出2个强连通分量:{3},{1,2,4}。

tarjan核心代码

void tarjan(int x){
	dfn[x]=low[x]=IN++;
	in_s[x]=1;
	S.push(x);
	for(int i=0;i<G[x].size();i++){
		if(!dfn[G[x][i]]){
			tarjan(G[x][i]);
			low[x]=min(low[x],low[G[x][i]]);
		}
		else if(in_s[G[x][i]])low[x]=min(low[x],dfn[G[x][i]]);
	}
	if(dfn[x]==low[x]){
		num++;int v;
		do{
			v=S.top();
			S.pop();
			belong[v]=num;
			M[num]=min(M[num],mn[v]);
			in_s[v]=0;
		}while(v!=x);
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/90715320