Tarjan算法

来源:互联网 发布:长沙源码信息要交钱吗 编辑:程序博客网 时间:2024/04/29 12:48

介绍

对任意有向图G=(V,E),我们定义强连通关系uv(这里u,vV),当且仅当存在uv的路径以及vu的路径。容易看到这是一个等价关系,因此可以将V划分成不相交的子集并,我们称每个子集为G的强连通分支。

Tarjan算法作用即是对任意有向图,求出其所有的强连通分支。且其时间复杂度为O(|V|+|E|)

算法

Tarjan算法本身十分简洁,但它的思想却非常巧妙。我们接下来介绍算法的具体内容:

算法的主体部分为深度优先搜索,在搜索的过程中对每个顶点维护两个属性lowlink和index。这里index表示深度优先搜索的访问顺序编号,即第n个访问的顶点index=n。lowlink是算法的关键所在,它的意义是记录从当前顶点所能到达的顶点中index最小的顶点。

算法同时维护一个栈,这个栈是tarjan算法的重要不变量,即顶点在栈中当且仅当它的lowlink对应的顶点也在栈中。

具体算法参考:维基百科。其关键步骤是:

  1. 用DFS访问每个顶点。
  2. 当访问到顶点v时,将其压栈。其index值即为当前访问序号,其lowlink值递归定义如下(初始时v.lowlink=v.index):
    • (v,w)Ew还未访问,那么(递归)访问w,并且令v.lowlink=min{v.lowlink,w.lowlink}
    • (v,w)Ew还留在栈中,那么说明其已被访问,此时令v.lowlink=min{v.lowlink,w.index}
  3. 若对于顶点v,(递归回来后)有v.lowlink=v.index。那么将栈pop到v(包括v),这些pop出来的顶点即构成一个强连通分支。

正确性

我们只需证明下面的结论:

  • v.lowlink=v.index时从v到栈顶的元素包含且只包含v所在强连通分支的所有元素。
  • 栈最终会清空(由于算法会最终会返回到出发顶点,这是显然的。)

证明:否则。若有某个v的连通分支所包含的顶点w未被访问过,那么存在从vw的路径,由DFS的性质算法会继续搜索而不会返回。若有某个分支顶点wv先访问,那么由于存在vw的路径,因此这个递归分支返回的lowlink值必定不大于w.index,即v.lowlinkw.index<v.index,矛盾!最后,我们证明任何分支顶点w,不会在算法返回到v之前被pop掉,且任意非分支顶点都会pop掉。这是因为由强连通性存在wv的路径,因此由前面的讨论我们有w.lowlinkv.index<w.index,这不满足pop的条件。而对于非分支点,其lowlink必定大于vindex(否则会使v.lowlink减小),因此在返回之前必定会pop掉。

同时由DFS的性质容易知道复杂度为O(|V|+|E|)

类似算法

Tarjan算法的思想可以应用到很多问题上去,比如求解图的欧拉回路。我们可以给出算法如下:

  1. 用DFS访问所有边
  2. 若顶点v被访问,那么将其压栈。若此时v没有可访问的边,那么将其出栈。

容易知道若图存在欧拉回路,那么上面算法产生的序列即为一个回路。

另一个应用是求出无向图的无桥边连通分支(这个是直接应用)。

0 0