求有向图强连通分量的两个算法

tarjan算法、Kosaraju算法——求有向图强连通分量

Tarjan算法

Tarjan算法是一种基于DFS的算法,使用栈来存储访问过程中的顶点

先引入两个非常重要的数组:dfnlow

dfn:顶点被搜索到的“时间戳”,用于记录顶点被搜到的次序,一个顶点的时间戳记录后就不再改变

low:在本次DFS搜索中(最外层循环),且仍在栈中的最小时间戳,像是确立了一个关系,low相等的点在同一强连通分量中。

注意初始化时 dfn= low = ++cnt

算法思路:

图不一定是强连通图,所以跑Tarjan时要枚举每个点,若dfn == 0,进行深搜。

对于搜到的顶点,寻找以其为弧尾的顶点,判断这些顶点是否已经被搜索过,若没有(显然也一定没有入栈),则进行搜索;若该点已经入栈,说明形成了环,则更新low

在DFS回溯时不断比较low,不断取最小的low值。如果dfn[x]==low[x] 则说明找到了一个强连通分量,并可以以x为起点。对栈进行弹出操作,直到x被弹出,这些被弹出的顶点构成一个强连通分量的顶点集

递归函数代码:

void tarjan(int now)
{
dfn[now]=low[now]=++cnt; //初始化
stack[++t]=now;       //入栈操作
v[now]=1;         //v[]代表该点是否已入栈
for(int i=f[now];i!=-1;i=e[i].next) //邻接表存图
if(!dfn[e[i].v])           //判断该点是否被搜索过
{
tarjan(e[i].v);
low[now]=min(low[now],low[e[i].v]); //回溯时更新low[ ],取最小值
}
else if(v[e[i].v])
low[now]=min(low[now],dfn[e[i].v]); //一旦遇到已入栈的点,就将该点作为连通量的根
                        //这里用dfn[e[i].v]更新的原因是:这个点可能
                        //已经在另一个强连通分量中了但暂时尚未出栈,所
                        //以now不一定能到达low[e[i].v]但一定能到达
                        //dfn[e[i].v].
if(dfn[now]==low[now])
{
int cur;
do
{
cur=stack[t--];
v[cur]=false; //不要忘记出栈
}while(now!=cur);
}
}

Kosaraju算法

Kosaraju给出的方案:对原图取反,然后从反向图的任意节点开始进行DFS的逆后序遍历。根据逆后序遍历得到的序列进行DFS遍历(与无向图找连通分量类似)一定可以找到各强连通分量。

DFS的逆后序遍历是指:如果当前顶点未访问,先遍历完与当前顶点相连的且未被访问的所有其它顶点,然后将当前顶点加入栈中,最后栈中从栈顶到栈底的顺序就是我们需要的顶点顺序。

也就是说,使用Kosaraju算法求强连通分量分为两步

  • 对原图取反,从任意一个顶点开始对反向图进行逆后续DFS遍历
  • 按照逆后续遍历中栈中的顶点出栈顺序,对原图进行DFS遍历,一次DFS遍历中访问的所有顶点都属于同一强连通分量


参考文章链接
Kosaraju算法解析: 求解图的强连通分量
Tarjan算法 详解+心得

posted @   GeorgeHu6  阅读(99)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示