Kosaraju 算法学习笔记(求强连通分量)

写起来简单无比,不比 Tarjan 香?

方法

  1. 按照[1...n]的顺序在反图(边方向相反)上dfs一遍,出栈时将节点存入数组q[1...n]中
  2. 按照q[n...1]的顺序在原图上dfs一遍,每次遍历就是一个新的强联通分量

为什么是正确的?

核心在于封死连通分量往外走的路。

如果原图u-->v有一条边,且u和v不在同一个强联通分量里,那么反图v-->u有边,即u在q序列的位置一定在v的前面,那么在原图上逆序遍历q数组时一定先访问到v,再访问u,在dfs(v)时不会访问到u点(因为u和v不在同一个强联通分量里,v不能到达u),保证了算法的正确性。

核心代码

struct node{
	int v,next;
}e[2][maxn];
void insert(int T,int u,int v){
	cnt[T]++;
	e[T][cnt[T]].v=v;
	e[T][cnt[T]].next=p[T][u];
	p[T][u]=cnt[T];
}
void dfs1(int u){
	vis[u]=1;
	for(int i=p[1][u];i!=-1;i=e[1][i].next){
		int v=e[1][i].v;
		if(!vis[v]) dfs1(v);
	}
	q[++tot]=u;
}
void dfs2(int u,int RT){
	f[v]=RT;
	vis[u]=1;
	for(int i=p[0][u];i!=-1;i=e[0][i].next){
		int v=e[0][i].v;
		if(!vis[v]) dfs2(v,RT);
	}
}
void Kosaraju(){
	int n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		insert(0,u,v);//原图 
		insert(1,v,u);//反图 
	}
	memset(vis,0,sizeof(vis));tot=0;
	for(int i=1;i<=n;i++) if(!vis[i]) dfs1(i);
	memset(vis,0,sizeof(vis));tot=0;
	for(int i=n;i>=1;i--) if(!vis[q[i]]) rt[++tot]=q[i],dfs2(q[i],tot);
}
posted @ 2023-12-07 19:41  尹昱钦  阅读(24)  评论(0编辑  收藏  举报