zoukankan      html  css  js  c++  java
  • Tarjan的强连通分量算法

      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表示顶点所处强连通分量的代表顶点。

      算法的流程如下:

     1 tarjan(u, stk)
     2   if(u.dfn != 0)
     3     return
     4   u.dfn = order()
     5   u.low = u.dfn
     6   u.instk = true  
     7   stk.push(u)
     8   for (u, v) in E
     9     targin(v)
    10     if(v.instk)
    11       u.low = min(u.low, v.low)
    12   if(u.dfn == u.low)
    13     while(true)
    14       top = stk.pop()
    15       top.instk = false
    16       top.p = u
    17       if(top == u)
    18         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流程时,将会保证强连通分量的正确分离。

      

  • 相关阅读:
    Makefile 跟着走快点
    MariaDB 复合语句和优化套路
    Unity Shader常用函数,标签,指令,宏总结(持续更新)
    ThreadLocal 简述
    Java全排列排序
    Thrift入门
    Nginx + Keepalived 双机热备
    Linux 虚拟IP
    Java 反编译
    Spring拦截器
  • 原文地址:https://www.cnblogs.com/dalt/p/7912847.html
Copyright © 2011-2022 走看看