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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章