就我所知,有三种时间复杂度为O(n)的方法可以求强连通分量,分别是Kosaraju、Tarjan和Gabow。
- Kosaraju
算法的步骤为- 对图G进行DFS,并按照遍历完成的先后顺序进行标号。
- 将图G中所有的边反向得到G'。
- 对G'进行DFS,每轮DFS都选择编号最大的点最为当前的遍历树的根。
- 最后,遍历得到的森林就是SCC的集合。
- Tarjan
第一个求解SCC的算法,应用非常广泛,几乎任何和图的遍历有关的问题都可以套用Tarjan算法的思想(比如,求割点、桥、块等等)。
在遍历时,对每一个节点定义时间戳,同时定义Low函数,含义为节点及其子孙通过非父子边的返祖边所能到达的最小时间戳。最后若Low函数等于其时间戳,则当前递归栈内存在一个SCC。
代码如下:
12345678910111213141516171819voiddfs(intindex) {low[index] = dfn[index] = cnt++;used[index] = 1;del[index] = 0;st[top++] = index;for(inti = last[index]; i!=-1; i = e[i].nxt)if(!used[e[i].y]) {dfs(e[i].y);if(low[index]>low[e[i].y]) low[index] = low[e[i].y];}elseif(!del[e[i].y])if(low[index]>dfn[e[i].y])low[index] = dfn[e[i].y];if(low[index]==dfn[index]) {do{root[st[--top]] = index;del[st[top]] = 1;}while(st[top]!=index);}} - Gabow
算法的原理说起来太麻烦,还是直接给出代码。其实这个算法并不是很常用。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include <cstdio>#include <cstring>#include <stack>usingnamespacestd;constintMAXN = 1000;structnode {inttar;node *nxt;node(int_tar,node *_nxt): tar(_tar),nxt(_nxt) {}};node *g[MAXN];intn,m,co,all,dfn[MAXN],id[MAXN];stack<int> vec,path;voiddfs(intx) {dfn[x] = co++;vec.push(x);path.push(x);for(node *i = g[x]; i!=NULL; i = i->nxt)if(dfn[i->tar]==-1) dfs(i->tar);elseif(id[i->tar]==-1)while(dfn[path.top()]>dfn[i->tar]) path.pop();if(path.top()==x) path.pop();elsereturn;for(inti; ;) {id[i = vec.top()] = all;vec.pop();if(i==x)break;}all++;}intmain() {freopen("gabow.in","r",stdin);freopen("gabow.out","w",stdout);scanf("%d%d",&n,&m);memset(g,0,sizeof(g));for(inti = 0,a,b; i<m; i++) {scanf("%d%d",&a,&b); a--; b--;g[a] =newnode(b,g[a]);}co = all = 0;memset(dfn,-1,sizeof(dfn));memset(id,-1,sizeof(id));for(inti = 0; i<n; i++)if(dfn[i]==-1) dfs(i);for(inti = 0; i<n; i++)printf("%d\n",id[i]);return0;}
本文介绍了三种时间复杂度为O(n)的强连通分量(SCC)算法:Kosaraju、Tarjan和Gabow。Kosaraju算法通过两次深度优先搜索(DFS)实现;Tarjan算法利用时间戳和Low函数确定SCC;Gabow算法则通过对DFS的改进来发现SCC。
518

被折叠的 条评论
为什么被折叠?



