求有向图强连通分量的两个算法
tarjan算法、Kosaraju算法——求有向图强连通分量
Tarjan算法
Tarjan算法是一种基于DFS的算法,使用栈来存储访问过程中的顶点
先引入两个非常重要的数组:dfn 与 low
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遍历中访问的所有顶点都属于同一强连通分量
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署