PS:在贴出代码之前,我得说明内容来源——哈尔滨工业大学出版的《图论及应用》。虽然有一些错误的地方,但是不得不说是初学者该用的书。
从效率的角度来说,Kosaraju <Tarjan<Garbow。一般网上有前两种的代码和分析。Garbow算法是Tarjan的另一种实现,但是Garbow效率更高。
不过从复杂度来说,三种算法的时间(空间)复杂度都是O(m +n)。
模版的调用方式很简单,初始化,建图,调用Tarjan(n)或者Kosaraju(n)或者 Garbow(n), scc就是强连通分量的个数。
(1)Kosaraju
//Kosaraju const int N =10010, M=50010; struct node { int to, next; }edge[M],edge2[M]; //edge是逆图,edge2是原图 int dfn[N], head[N], head2[N], belg[N], num[N]; //dfn时间戳 //belg记录每个点属于哪个连通分量,num记录强连通分量点的个数 bool vis[N]; int cnt,cnt1,scc,tot,tot1; void dfs1(int u) { vis[u]=1; for(int k=head2[u];k!=-1;k=edge2[k].next) if(!vis[edge2[k].to]) dfs1(edge2[k].to); dfn[++cnt1]=u; } void dfs2(int u) { vis[u]=1; cnt++; belg[u]=scc; for(int k=head[u];k!=-1;k=edge[k].next) if(!vis[edge[k].to]) dfs2(edge[k].to); } void Kosaraju(int n) { memset(dfn,0,sizeof(dfn)); memset(vis,0,sizeof(vis)); cnt1=scc=0; for(int i=1;i<=n;i++) if(!vis[i]) dfs1(i); memset(vis,0,sizeof(vis)); for(int i=cnt1;i>0;i--) if(!vis[dfn[i]]) { cnt=0; ++scc; dfs2(dfn[i]); num[scc] = cnt; } } void init() { tot=tot1=0; memset(head,-1,sizeof(head)); memset(head2,-1,sizeof(head2)); memset(num,0,sizeof(num)); } void addedge(int i,int j) { edge2[tot1].to=j; edge2[tot1].next=head2[i];head2[i]=tot1++; edge[tot].to=i; edge[tot].next=head[j];head[j]=tot++; }
(2)Tarjan
//Tarjan const int N =1010, M=100010; struct node { int to, next; }edge[M]; int head[N], low[N], dfn[N], sta[N], belg[N], num[N]; bool vis[N]; int scc,index,top, tot; void tarbfs(int u) { int i,j,k,v; low[u]=dfn[u]=++index; sta[top++]=u; vis[u]=1; for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(!dfn[v]) { tarbfs(v); if(low[u]>low[v]) low[u]=low[v]; } else if(vis[v]&&low[u]>dfn[v]) low[u]=dfn[v]; } if(dfn[u]==low[u]) { scc++; do { v=sta[--top]; vis[v]=0; belg[v]=scc; num[scc]++; } while(v!=u) ; } } void Tarjan(int n) { memset(vis,0,sizeof(vis)); memset(dfn,0,sizeof(dfn)); memset(num,0,sizeof(num)); memset(low,0,sizeof(low)); index=scc=top=0; for(int i=1;i<=n;i++) if(!dfn[i]) tarbfs(i); } void init() { tot=0; memset(head,-1,sizeof(head)); } void addedge(int i,int j) { edge[tot].to=j; edge[tot].next=head[i];head[i]=tot++; }
(3)Garbow
//Garbow const int N =110, M=100010; struct node { int to, next;; }edge[M]; int stk[N],stk2[N],head[N],low[N],belg[N]; int cn,cm,tot,scc,lay; int Garbowbfs(int cur,int lay) { stk[++cn]=cur; stk2[++cm]=cur; low[cur]=++lay; for(int i=head[cur];i!=-1;i=edge[i].next) { int v=edge[i].to; if(!low[v]) Garbowbfs(v,lay); else if(!belg[v]) while(low[stk2[cm]]>low[v]) cm--; } if(stk2[cm]==cur) { cm--; scc++; do belg[stk[cn]]=scc; while(stk[cn--]!=cur) ; } return 0; } void Garbow(int n) { scc=lay=0; memset(belg,0,sizeof(belg)); memset(low,0,sizeof(low)); for(int i=0;i<n;i++) if(!low[i]) Garbowbfs(i,lay); } void init() { memset(head,-1,sizeof(-1)); } void addedge(int i,int j) { edge[tot].to=j; edge[tot].next=head[i];head[i]=tot++; }