Tarjan算法用于寻找图G(V,E)中的所有强连通分量,其时间复杂度为O(|V|+|E|)。

  所谓强连通分量就是V的某个极大子集,其中任意两个结点u,v在图中都存在一条从u到v的路径。

  Tarjan的算法的流程是通过深度优先搜索遍历每个顶点,并且维护以下属性dfn,low,instk,p其中dfn表示该顶点第一次被访问时的次序,instk需要与一个栈stk配合使用,stk用于记录从某个顶点出发,尚未被包含进强连通分量的所有顶点,而instk用于记录一个顶点是否还存在于stk中,low表示从该结点出发可以访问到的所有在栈中的顶点中dfn属性最小的顶点的dfn值,p表示顶点所处强连通分量的代表顶点。

  算法的流程如下:

 tarjan(u, stk)
  if(u.dfn != 0)
    return
  u.dfn = order()
  u.low = u.dfn
  u.instk = true  
  stk.push(u)
  for (u, v) in E
    targin(v)
    if(v.instk)
      u.low = min(u.low, v.low)
  if(u.dfn == u.low)
    while(true)
      top = stk.pop()
      top.instk = false
      top.p = u
      if(top == u)
        break

  其中order()表示分配下一个次序号,要求order()方法的返回值随调用次数增加而递增,且不能少于1,可以通过维护一个计数器实现。我们需要对每个V中的顶点调用上述Tarjan流程即可保证强连通分量的正确分离。

  说明时间复杂度,由于每个结点被访问都会设置dfn值,因此一个结点最多只会被访问一次,其4~7行总执行次数不可能超过|V|。而8~11行中每次都会使用一条完全不同的边,其总执行次数不可能超过|E|。12~18行每次循环都会令一个顶点弹出stk,由于只有4~7行会向栈中压入一个顶点,因此总执行次数不会超过|V|。因此总的时间复杂度为O(|V|+|E|)。

  再说明算法正确性。从两个角度说明:1.任意两个连通顶点u,v都会拥有相同的p属性值,即u.p=v.p。2.任意两个不连通顶点都会拥有不同的p属性值。

  命题1:对于栈中的元素x,y,若x.dfn<y.dfn,则x必定在y之后出栈。因为dfn属性与入栈的顺序是一致的。

  命题2:若顶点x被加入栈中,则栈中所有现存顶点到x都有一条路径。假设当栈中所有顶点满足命题时,我们通过栈中的某个顶点y,将其后置顶点x加入到栈中,由于假设可知栈中y及y之下所有的顶点都能访问到x。对于y之上的第一个顶点z,若z不为x,则由于z在回溯到y时,没有从栈中弹出,故z.dfn>z.low,即z能访问到z之下的某个顶点,故z能访问到x。因此由归纳法可知命题成立。

  命题3:当我们确定了栈中某个顶点u的low值时,在栈中u之上所有的顶点和u必定处在同一个强连通分量中。假设当栈中所有顶点满足这一性质时,我们压入顶点u,并利用深度优先搜索算法遍历u的后置顶点。当我们确定了u的low值时,若在栈中u之上还存在顶点v,不妨设v为u之上的第一个顶点,显然v.dfn>u.dfn,即v的回溯应该发生在u回溯之前,而v没有被出栈,意味着v.low<v.dfn,即v能访问到栈中某个v之下的顶点z,v和u是连通的。依旧是使用了归纳法。

  对于1,不妨设u.dfn<v.dfn,由于v能访问到u,故v.low<=u.dfn,而由命题2知道,所有栈中v之下的顶点x都满足x.low<=v.low<=u.dfn,即v出栈时必定会导致u的出栈,故v.p=u.p。

  对于2,当u和v被设置相同的p值时,意味着二者同时出栈。而由命题3可知u和v必定是连通的。

  因此当我们对V中每个顶点调用Tarjan流程时,将会保证强连通分量的正确分离。

  

Tarjan的强连通分量算法的更多相关文章

  1. 【学习整理】Tarjan:强连通分量+割点+割边

    Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 ...

  2. Tarjan求强连通分量,缩点,割点

    Tarjan算法是由美国著名计算机专家发明的,其主要特点就是可以求强连通分量和缩点·割点. 而强联通分量便是在一个图中如果有一个子图,且这个子图中所有的点都可以相互到达,这个子图便是一个强连通分量,并 ...

  3. CCF 高速公路 tarjan求强连通分量

    问题描述 某国有n个城市,为了使得城市间的交通更便利,该国国王打算在城市之间修一些高速公路,由于经费限制,国王打算第一阶段先在部分城市之间修一些单向的高速公路. 现在,大臣们帮国王拟了一个修高速公路的 ...

  4. Tarjan求强连通分量、求桥和割点模板

    Tarjan 求强连通分量模板.参考博客 #include<stdio.h> #include<stack> #include<algorithm> using n ...

  5. UESTC 901 方老师抢银行 --Tarjan求强连通分量

    思路:如果出现了一个强连通分量,那么走到这个点时一定会在强连通分量里的点全部走一遍,这样才能更大.所以我们首先用Tarjan跑一遍求出所有强连通分量,然后将强连通分量缩成点(用到栈)然后就变成了一个D ...

  6. tarjan求强连通分量+缩点+割点以及一些证明

    “tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往”----<膜你抄>   自从听完这首歌,我就对tarjan开始心驰神往了,不过由于之前水平不足,一 ...

  7. tarjan求强连通分量+缩点+割点/割桥(点双/边双)以及一些证明

    “tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往”----<膜你抄>   自从听完这首歌,我就对tarjan开始心驰神往了,不过由于之前水平不足,一 ...

  8. HDU 1827 Summer Holiday(tarjan求强连通分量+缩点构成新图+统计入度+一点贪心思)经典缩点入门题

    Summer Holiday Time Limit: 10000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  9. UVALive 4262——Trip Planning——————【Tarjan 求强连通分量个数】

    Road Networks Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Stat ...

随机推荐

  1. [置顶] 个人博客上线!欢迎来访~ http://onlyloveyd.cn/

    简介 Hexo + Github + 个人域名 构建静态博客系统. 构建方法 参考 https://yq.aliyun.com/articles/64953 个人博客网站 Cherish Androi ...

  2. poj2378(dfs,树形dp)

    和poj3107,poj1655一样的方法 #include<iostream> #include<cstdio> #include<cstdlib> #inclu ...

  3. VSCode打开文件总是会覆盖上次打开的标签

    在使用VSCode的时候,打开一个文件之后,如果没有修改的话,那么再打开下一个文件的时候,他总会替换上次打开的标签,那么怎么样才能每次都在新的标签打开文件呢? 实际上,这种情况的出现是因为我们点击文件 ...

  4. 通用线程:POSIX 线程详解,第 3 部分

    通用线程:POSIX 线程详解,第 3 部分 使用条件变量提高效率 Daniel Robbins, 总裁兼 CEO, Gentoo Technologies, Inc. 简介: 本文是 POSIX 线 ...

  5. NGINX 配置文件配置url重写

    1.项目在根目录: location / {            index  index.html index.htm index.php l.php;            autoindex  ...

  6. 使用Oracle 11g新特性 Active Database Duplication 搭建Dataguard环境

    Duplication Database 介绍 Duplicate database可以按照用途分为2种: duplicate database(复制出一个数据库) duplicate standby ...

  7. Python学习-数据运算

    在Python中有丰富的算术运算,这使得Python在科学计算领域有着很高的地位,Python可以提供包括四则运算在内的各种算术运算. a = 10 b = 20 print(a-b) #-10 pr ...

  8. 《DSP using MATLAB》示例Example7.20

    代码: M = 51; alpha = (M-1)/2; Dw = 2*pi/M; l = 0:M-1; wl = Dw*l; T1 = j*0.39; k1 = 0:floor((M-1)/2); ...

  9. C# chart控件运用

    为了弄一个实时数据显示的窗口,最近一周时间都耗在这个控件上了,属性有点多(下面列的是一些常用的) 后来干脆写代码把他们封装起来,各个chart直接来调用它,省得到属性里面去设置. chart内的一些元 ...

  10. spring切面配置,代理用jdk和cglib的区别

    jdk的动态代理大家应该都听说过,条件是必须要有接口:cglib不要求接口,那么它是怎么实现切面的呢?很简单,通过继承,它动态的创建出一个目标类的子类,复写父类的方法,由此实现对方法的增强.看例子: ...