算法实现:
该算法通过两个搜索作为主体实现,第一个搜索来遍历原图的每一个节点,找到强连通分量中任意一个节点,以及其它子图中的顶点。我们将其编号,最大的号入度为0的点,它一定是某子图的顶点,若有多个,则顺序任意,接下来将这个些点去掉,接着找入度为0的点。
我们并不需要真的用到拓扑排序,只需要将每个点进队,dfs结束后队尾为搜索最先遍历到的点。它一定是顶点。这和上面的拓扑思路是一致的,当一个子图结束,进行下一个子图时,虽然当前子图的非顶点会在上一个子图的顶点之后,但是这些点是联通的,我们从后往前遍历,一个子图结束,下一个开始的点一定是下一个子图的顶点。
void dfs1(int u)
{if(vis[u]) return ; //若该点已经被遍历,则dfs开始回退vis[u]=1;for(int i=0;i<G[u].size();i++){int y=G[u][i];dfs1(y);}s.push_back(u); //最后的点在前面。
}
第二个搜索从反图上开始遍历上面找到的点,由于是反图,所以遍历点i时,整个以i为顶点的子图都被困住,在原图上入度为0的,在反图上会直接停在原地,这直接就是一个强连通分量,当i是一个回路,那反图同样是回路,所以整个回路都是一个强连通分量。
void dfs2(int u)
{if(sccno[u]) return ; //标记,该点以在一个SCC中sccno[u]=cnt;for(int i=0;i<rG[u].size();i++)dfs2(rG[u][i]);
}
整个代码:
void Kosaraju(int n)
{cnt=0;s.clear();memset(sccno,0,sizeof(sccno));memset(vis,0,sizeof(vis));for(int i=1;i<=n;i++)dfs1(i);for(int i=n-1;i>=0;i--)if(!sccno[s[i]]) {cnt++;dfs2(s[i]);}
}