Ksaraju算法——图的SCC

算法实现:

  该算法通过两个搜索作为主体实现,第一个搜索来遍历原图的每一个节点,找到强连通分量中任意一个节点,以及其它子图中的顶点。我们将其编号,最大的号入度为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]);}
}

posted @ 2025-03-31 23:03  _窗帘  阅读(28)  评论(0)    收藏  举报