Garbow算法是Tarjan算法的另一种实现,Tarjan算法里面的用dfn[maxn]和low[maxn]数组来求出强连通分量的根。Garbow在编程时是用一个堆栈来辅助求出强连通分量的根。
使用类比方法,在Tarjan算法中,每次low[v]的修改都是由于环的出现,不然,low[v]的值不可能变小。每次出现环,在这个环里面只剩下一个low[v]没有被改变(深度最低的那个),或者全部改变,如果深度最低的节点在另一个环内。那么Garbow算法中的第二个堆栈变化就是删除构成环的的节点,只剩下深度最低的节点,或者全部删除。这个过程通过出栈来实现。因为深度最低的那个顶点一定比前面的先访问,那么只要出栈一直到栈顶那个顶点的访问时间不大于深度最低的那个顶点,其中每个被弹出的节点属于同一个强连通分量。因为这个节点访问之前,能够构成强连通分量的那些节点已经被弹出了。Tarjan中的判断条件用第二个堆栈弹出的顶点是不是当前顶点来代替。
Tarjan算法和Garbow算法是同一个思想的不同实现,但是Garbow算法更加精妙,时间更少,不用频繁更新low。
算法步骤:
1.找一个没有被访问过的节点,否则,算法结束。
2.将v压入堆栈stk1[]和stk2[]
3.对于所有的邻接顶点
(1).如果没有访问过,转入步骤2
(2).如果访问过,但没有删除,维护stk2[](处理环的过程),如果stk2[]的顶元素等于v,那么输出相应的强连通分量。
时间复杂度分析:O(n+m)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=110;
const int maxm=100010;
struct edge{
int v,next;
};
edge edges[maxm];
int head[maxn],tot;
int stk[maxn],stk2[maxn],belg[maxn],low[maxn];
int n,m,cn,cm,scc,lay;
void add_edge(int u,int v)
{
edges[tot].v=v;
edges[tot].next=head[u];
head[u]=tot++;
}
void garbowbfs(int cur,int temper)
{
stk[++cn]=cur;
stk2[++cm]=cur;
low[cur]=++lay;
for(int i=head[cur];~i;i=edges[i].next){
int v=edges[i].v;
if(!low[v]){
garbowbfs(v,lay);
}else if(!belg[v]){
while(low[stk2[cm]]>low[v]){
cm--;
}
}
}
if(stk2[cm]==cur){
cm--;
scc++;
printf("%d: ",scc);
do{
printf("%d ",stk[cn]);
belg[stk[cn]]=scc;
}while(stk[cn--]!=cur);
printf("\n");
}
}
int garbow()
{
scc=lay=0;
memset(belg,0,sizeof(belg));
memset(low,0,sizeof(low));
for(int i=1;i<=n;i++){
if(!low[i]){
garbowbfs(i,lay);
}
}
return scc;
}
int main()
{
scanf("%d%d",&n,&m);
int u,v;
memset(head,-1,sizeof(head));
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
add_edge(u,v);
}
int ans=garbow();
printf("%d",ans);
return 0;
}