说到以Tarjan命名的算法,我们经常提到的有3个,其中就包括本文所介绍的求强连通分量的Tarjan算法。而提出此算法的普林斯顿大学的Robert E Tarjan教授也是1986年的图灵奖获得者(具体原因请看本博“历届图灵奖得主”一文)。
首先明确几个概念。
关于Tarjan算法的伪代码和流程演示请到我的115网盘下载网上某大牛写的Doc(地址:http://u.115.com/file/f96af404d2<Tarjan算法.doc>)本文着重从另外一个角度,也就是针对tarjan的操作规则来讲解这个算法。
其实,tarjan算法的基础是DFS。我们准备两个数组Low和Dfn。Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的Dfn值(很绕嘴,往下看你就会明白),Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。根据以下几条规则,经过搜索遍历该图(无需回溯)和对栈的操作,我们就可以得到该有向图的强连通分量。
由于每个顶点只访问过一次,每条边也只访问过一次,我们就可以在O(n+m)的时间内求出有向图的强连通分量。但是,这么做的原因是什么呢?
Tarjan算法的操作原理如下:
参考代码:
program tarjan; var v,f:array[1..100]of boolean; dfn,low:array[1..100]of integer; a:array[0..100,0..100]of integer; //边表 i,j,n,m,x,y,deep,d:integer; stack,ln:array[1..100]of integer; function min(x,y:longint):integer; begin if x>y then exit(y) else exit(x); end; procedure print(x:integer); //出栈,打印 begin while stack[deep]<>x do begin write(stack[deep],' '); f[stack[deep]]:=false; dec(deep); end; writeln(stack[deep]); f[stack[deep]]:=false; //去除入栈标记 dec(deep); end; procedure dfs(x:integer); var i:integer; begin inc(d); //时间 dfn[x]:=d; //规则1 low[x]:=d; inc(deep); //栈中元素个数 stack[deep]:=x; //规则2 f[x]:=true; for i:=1 to a[x,0] do if not v[a[x,i]] then begin v[a[x,i]]:=true; dfs(a[x,i]); low[x]:=min(low[a[x,i]],low[x]); //规则3 end else if f[a[x,i]] then low[x]:=min(low[x],dfn[a[x,i]]); //规则4 if dfn[x]=low[x] then //规则5 print(x); end; begin readln(n,m); fillchar(a,sizeof(a),0); for i:=1 to m do begin readln(x,y); //读入图 inc(a[x,0]); a[x,a[x,0]]:=y; end; for i:=1 to n do if not v[i] then begin v[i]:=true; dfs(i); //更换起点,规则6 end; end.
本文地址:http://www.cnblogs.com/saltless/archive/2010/11/08/1871430.html
(saltless原创,转载请注明出处)