【ACM】tarjan算法详解【强连通分量】

转载 2013年12月03日 11:13:28
呃、有时间在好好看看。
原文地址:tarjan算法详解作者:cccc

Tarjan算法详解

效率确实是太低了,居然学习这个算法也用了我一个下午,很大的原因应该是没有找到好的课件或者说理解能力有待提高吧!

说实话,其实tarjan算法也比较简单,下面大概说说tarjan算法吧:

【功能】

   Tarjan算法的用途之一是,求一个有向图G=(V,E)里极大强连通分量。强连通分量是指有向图G里顶点间能互相到达的子图。而如果一个强连通分量已经没有被其它强通分量完全包含的话,那么这个强连通分量就是极大强连通分量。

【算法思想】

   dfs遍历G中的每个顶点,通dfn[i]表示dfs时达到顶点i的时间,low[i]表示i所能直接或间接达到时间最小的顶点。(实际操作中low[i]不一定最小,但不会影响程序的最终结果)

   程序开始时,time初始化为0,在dfs遍历到v时,low[v]=dfn[v]=time++

v入栈(这里的栈不是dfs的递归时系统弄出来的栈)扫描一遍v所能直接达到的顶点k,如果k没有被访问过那么先dfs遍历klow[v]=min(low[v],low[k]);如果k在栈里,那么low[v]=min(low[v],dfn[k])(就是这里使得low[v]不一定最小,但不会影响到这里的low[v]会小于dfn[v])。扫描完所有的k以后,如果low[v]=dfn[v]时,栈里v以及v以上的顶点全部出栈,且刚刚出栈的就是一个极大强连通分量。

【大概的证明】

 1 在栈里,当dfs遍历到v,而且已经遍历完v所能直接到达的顶点时,low[v]=dfn[v]时,v一定能到达栈里v上面的顶点:

   因为当dfs遍历到v,而且已经dfs递归调用完v所能直接到达的顶点时(假设上面没有low=dfn),这时如果发现low[v]=dfn[v],栈上面的顶点一定是刚才从顶点v递归调用时进栈的,所以v一定能够到达那些顶点。

 

2 .dfs遍历时,如果已经遍历完v所能直接到达的顶点而low[v]=dfn[v],我们知道v一定能到达栈里v上面的顶点,这些顶点的low一定小于自己的dfn,不然就会出栈了,也不会小于dfn[v],不然low [v]一定小于dfn[v],所以栈里v以其v以上的顶点组成的子图是一个强连通分量,如果它不是极大强连通分量的话low[v]也一定小于dfn[v](这里不再详细说),所以栈里v以其v以上的顶点组成的子图是一个极大强连通分量。

【时间复杂度】

    因为所有的点都刚好进过一次栈,所有的边都访问的过一次,所以时间复杂度为On+m

 

【参考代码(pascal)】

  

type
  emap=record  //边表
   v1,v2,next:longint;
  end;
  vmap=record  //first表示顶点v的第一条边在边表的位置,last表示最后一条边的位置
   first,last:longint;
  end;
var
  e:Array[1..10000] of emap;
  a:array[1..100] of vmap;
  dfn,low:array[1..100] oflongint;
 i,j,n,m,v1,v2,ei,zi,time:longint;  //zi表示栈中元素的数量,time表示dfs的时间
  zhan:Array[1..1000] oflongint; //栈堆
  visit:array[1..100] ofboolean; //true表示有被dfs遍历过
  zvisit:array[1..100] of longint;//表示顶点v在栈中的什么位置
functionmin(a,b:longint):longint;
begin
  if a<b then exit(a)else exit(b);
end;
procedurepush(v:longint); //把v进栈
begin
  inc(zi);
  zvisit[v]:=zi;
  zhan[zi]:=v;
end;
procedurechuzhan(v:longint);   //把v以及v以上的全部出栈
var
  lin:longint;
begin
  lin:=zvisit[v];
  for i:=lin to zi do
  begin
    ifi=zi then writeln(zhan[i])
   else write(zhan[i],' ');
   zvisit[zhan[i]]:=0;
  end;
  zi:=lin-1;
end;
proceduredfs(v:longint);
var
  i,j,zhi,k:longint;
begin
  inc(time);
  dfn[v]:=time; low[v]:=time;
  zhi:=a[v].first;
  visit[v]:=true;
  push(v);
  whilezhi<>-1 do
  begin
   k:=e[zhi].v2;
    ifnot visit[k] then   //如果k没有被访问过
   begin
     dfs(k);    //dfs遍历k
     low[v]:=min(low[v],low[k]); 
   end else
    if (zvisit[k]>0)then   //如果k在栈里
   low[v]:=min(low[v],dfn[k]);
   zhi:=e[zhi].next;
  end;
  if low[v]=dfn[v] then
   chuzhan(v);     //把v及以上的顶点出栈并输出
end;
begin
  readln(n,m);  //读入顶点数和边数
  for i:=1 to n doa[i].first:=-1;
  ei:=0;
  for i:=1 to m do   //读入边,并把它保存到特殊边表里
  begin
   readln(v1,v2);
    ifa[v1].first=-1 then
   begin
     inc(ei);
     e[ei].v1:=v1; e[ei].v2:=v2;
     e[ei].next:=-1;
     a[v1].first:=ei; a[v1].last:=ei;
   end else
   begin
     inc(ei);
     e[ei].v1:=v1; e[ei].v2:=v2;
     e[ei].next:=-1;
     e[a[v1].last].next:=ei;
     a[v1].last:=ei;
   end;
  end;
 fillchar(visit,sizeof(visit),false);    //初始化全部都没有被访问过
 fillchar(zvisit,sizeof(zvisit),0);    //初始化全部都不在栈里
  time:=0;  zi:=0;  //初始化时间为0,栈为空
  for i:=1 to n do  //预防顶点1不能到达所有的点
  begin
    ifnot visit[i] then
   dfs(i);
  end;
end.


以上代码纯属照写,经优化后的核心代码:

functiongetfather(dep:longint):longint;
begin
  if father[dep]=dep then exit(dep);
  getfather:=getfather(father[dep]);
  father[dep]:=getfather;
end;
procedure dfs(dep:longint);
var
  i,j,v2:longint;
begin
  inc(time);
  low[dep]:=time;
  check[dep]:=true; zhan[dep]:=true;
  for i:=1 to n do
  begin
    if map[dep,i]=1 then
    begin
      if notcheck[i] then dfs(i);
      v2:=getfather(i);
      ifzhan[v2] then
      iflow[v2]<low[dep] then begin
       low[dep]:=low[v2];
       father[dep]:=v2;
     end;
    end;
  end;
  zhan[dep]:=false;
end;


强连通算法--Tarjan个人理解+详解

首先我们引入定义: 1、有向图G中,以顶点v为起点的弧的数目称为v的出度,记做deg+(v);以顶点v为终点的弧的数目称为v的入度,记做deg-(v)。 2、如果在有向图G中,有一条有向道路,则v称为...
  • mengxiang000000
  • mengxiang000000
  • 2016年06月14日 17:31
  • 4263

强连通分量-tarjan算法模板详解

这里主要给出以为大牛的分析,但是大牛的模板代码没有注释,我也比较笨,还是看了很久,这里给出模板的详细注释...
  • ZYY173533832
  • ZYY173533832
  • 2013年10月10日 19:05
  • 2890

强连通分量及缩点tarjan算法解析

强连通分量: 简言之 就是找环(每条边只走一次,两两可达) 孤立的一个点也是一个连通分量   使用tarjan算法 在嵌套的多个环中优先得到最大环( 最小环就是每个孤立点)   定义: int Ti...
  • qq574857122
  • qq574857122
  • 2013年11月16日 22:49
  • 10955

POJ 1236(tarjan 强连通分量 缩点)

POJ1236题目大意问,对于一个DAG(又向无环图): 1.至少要选几个点,才能从这些点出发到达所有点 2.至少加入几条边,就能从图中任何一个点出发到达所有点分析先求DAG的强连通分量数,再缩点...
  • mmy1996
  • mmy1996
  • 2017年02月22日 22:11
  • 443

有向图强连通分量 Tarjan算法【java实现】

注:本文章上部分内容转载自http://www.cppblog.com/sosi/archive/2010/09/26/127797.html;一方面是网上有很多关于tarjan算法的介绍,我觉得都没...
  • u013376508
  • u013376508
  • 2016年03月28日 09:59
  • 1827

对于Tarjan强连通分量算法的理解

对于Tarjan强连通分量算法的理解
  • Goseqh
  • Goseqh
  • 2017年02月22日 12:39
  • 883

Tarjan算法求强连通分量总结

Tarjan算法求强连通分量总结 首先明确强连通分量的概念:如果图中的任意两个点都能互相到达,则为强连通分量。极大强连通分量:不被其它任何强连通分量包含的强连通分量。 强连通分量主要与两种边有关:...
  • MrH929
  • MrH929
  • 2016年07月18日 19:12
  • 468

Tarjan算法【有向图的强连通分量】

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(stronglyconnected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图...
  • fang_jian_ming
  • fang_jian_ming
  • 2014年08月23日 09:38
  • 994

[转载] 有向图强连通分量的Tarjan算法 ——byvoid

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的...
  • u014204710
  • u014204710
  • 2014年04月14日 14:41
  • 569

搜索强连通分量_Tarjan算法_C++实现

在byvoid的博客上学习了下Tarjan算法,在未看byvoid给出的C++代码情况下,自行写了一个简单的实现 ^^ 注意: 为了快速编码,易阅读和理解代码,体会的算法的思路为主的思路下,我尽量...
  • yyr2008
  • yyr2008
  • 2016年01月09日 22:20
  • 661
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【ACM】tarjan算法详解【强连通分量】
举报原因:
原因补充:

(最多只允许输入30个字)