Tarjan算法

介绍

对任意有向图 G=(V,E) ,我们定义强连通关系 uv (这里 u,vV ),当且仅当存在 u v的路径以及 v u的路径。容易看到这是一个等价关系,因此可以将 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)E w 还未访问,那么(递归)访问w,并且令 v.lowlink=min{v.lowlink,w.lowlink}
    • (v,w)E w 还留在栈中,那么说明其已被访问,此时令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未被访问过,那么存在从 v w的路径,由DFS的性质算法会继续搜索而不会返回。若有某个分支顶点 w v先访问,那么由于存在 v w的路径,因此这个递归分支返回的lowlink值必定不大于 w.index ,即 v.lowlinkw.index<v.index ,矛盾!最后,我们证明任何分支顶点 w ,不会在算法返回到v之前被pop掉,且任意非分支顶点都会pop掉。这是因为由强连通性存在 w v的路径,因此由前面的讨论我们有 w.lowlinkv.index<w.index ,这不满足pop的条件。而对于非分支点,其 lowlink 必定大于 v index(否则会使 v.lowlink 减小),因此在返回之前必定会pop掉。

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

类似算法

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LCA(最近公共祖先)是指在一棵树中,找到两个节点的最近的共同祖先节点。而Tarjan算法是一种用于求解强连通分量的算法,通常应用于有向中。它基于深度优先搜索(DFS)的思想,通过遍历中的节点来构建强连通分量。Tarjan算法也可以用于求解LCA问题,在有向无环(DAG)中。 具体来说,在使用Tarjan算法求解LCA时,我们需要进行两次DFS遍历。首先,我们从根节点开始,遍历每个节点,并记录每个节点的深度(即从根节点到该节点的路径长度)。然后,我们再进行一次DFS遍历,但这次我们在遍历的过程中,同时进行LCA的查找。对于每个查询,我们将两个待查询节点放入一个查询列表中,并在遍历过程中记录每个节点的祖先节点。 在遍历的过程中,我们会遇到以下几种情况: 1. 如果当前节点已被访问过,说明已经找到了该节点的祖先节点,我们可以更新该节点及其所有后代节点的祖先节点。 2. 如果当前节点未被访问过,我们将其标记为已访问,并将其加入到查询列表中。 3. 如果当前节点有子节点,我们继续递归遍历子节点。 最终,对于每个查询,我们可以通过查询列表中的两个节点的最近公共祖先节点来求解LCA。 需要注意的是,Tarjan算法的时间复杂度为O(V+E),其中V为节点数,E为边数。因此,对于大规模的树结构,Tarjan算法是一种高效的求解LCA问题的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值