tarjan算法

一、tarjan求有向图强连通分量

这是tarjan算法应用最简单的一个方面了。
对于什么都不会的萌新,先介绍一下各个概念。
强连通:在一个有向图中,对于两个点i, j,如果存在路从i通向j,存在路从j通向i,那么称这两个点强连通。
如果一个图中任意两个点强连通,那么称这个图为强连通图。
一个图的极大子图满足强连通,那么称这个子图为强连通分量。

那么问题来了,我们怎么理解强连通分量呢?
其实,你可以找出一个环,这个环里的所有的点就组成了一个强连通分量。
这里写图片描述
比如这个图,在这个环2->3->4->2中,任意两个点能互相到达,那么2、3 、4就组成了这个图的一个强连通分量
注意:一个点也是强连通分量
所以,这个图的所有强连通分量就是:
set1: 1
set2: 2 3 4
set3: 5
共有三个强连通分量。

好的,回到正题,tarjan算法其实就是一个DFS的过程,现在,我们开一个数组,记录下我们遍历这个图的顺序。一般这个数组都叫DFN。
如果从1开始DFS,那么,我们把遍历的顺序写成一个DFS树是这样的(如果从其他点开始,DFN数组会不一样,但是结果是一样的),如图:
这里写图片描述
现在DFN数组里的状态为:
DFN[1] = 1, DFN[2] = 2, DFN[3] = 3, DFN[4] = 4, DFN[5] = 5.
代表了遍历的顺序。

很明显,图中是有一条回路的,这条回路在哪呢?
这里写图片描述
我们可以看到,在DFS树中,只要一个节点指向了他的一个父节点,那么肯定形成了回路,从起点到终点内包含的点组成了一个强连通分量。
现在的问题是,怎么找到有没有这样往回指的边,而且要找到这个回路的包含的点。
现在我们再来一个数组,一般叫low, low[i]表示i号节点能向上指的的节点中有最小DFN的那个节点 的DFN值,在上面那个图中,low[4] = 2。
那么三号节点呢?因为3号可以通过3->4->2指向2号节点,所以low[3] = 2。

注意:low的值不是节点的编号,而是那个节点的DFN值,在上面的那个情况中只是恰好i号节点的DFN值就是i。
也就是说,所有拥有相同low值的节点,在同一个强连通分量中,我们只要找出有相同low值的点组成的集合就可以了。

怎么找呢?
我们建一个栈,按照DFS的顺序把数push进栈里,只要在求完DFN和LOW数组后,在递归的过程中,碰见一个点i, 它的LOW[i] == DFN[i],那么就从栈里pop出数,直到pop出i,这些数就是low为DFN[i]的一个集合。
具体见图:
这里写图片描述
先看左图,遍历到4号的时候,发现碰见了2,能指回去,那么在递归的过程中就把low[3],和low[4] 都赋值为了2(DFN[2] = 2),然后后退到了2号节点,此时发现low[2] == dfn[2],此时开始pop,low[2],low[3], low[4]都是2,把4,3,2都pop出来,那么2,3,4就是一个强连通分量
用栈pop的过程,其实就是一个寻找相同low值的过程,这个过程比较难理解些,最好出个数据模拟,自己多思考些.


求割点和桥

还是先给概念:
割点:去掉某个点后,一个连通图被分解成两个不能互相到达的图,那么这个点就称为割点
桥(割边):去掉某条边后,一个连通图被分解成两个不能互相到达的图,那么这条边就成为桥
PS:割点和桥是无向图里的概念,所以下面提到的图是无向图.

这里写图片描述
假如这是一颗DFS树(即上面的DFN数组,虽然是无向图,但也有它的DFN数组)


桥:

如果一条边(u->v)是桥,那么下面的那个点能往上翻的最高的点,不能到达或超过上面的那个点

以上图(2->3)这条边为例,DFN[2] = 2, LOW[3] = 3,点3再怎么往上翻,也不能到达2或者2以上,那么,2->3就是一个桥
再看(3->4),DFN[3] = 3, Low[4] = 3,此时,下边的点4能往上翻到点3,也就是到达了点3,此时,(3,4)两个点有两条通路,(3->4), (3->5->4),也就不是桥

也就是:对于相邻的两个点u,v,若DFN[u] < Low[v],则有边(u->v)是桥.


割点:

如果你明白了上面的过程,那么割点就不难理解了.
如果一个点是割点,有两种情况:
1. 这个点是DFS树的根节点,那么只要这个点有两个儿子,这个点就是一个割点.
2. 这个点不是根节点,那么只要满足其所有儿子节点网上翻不能超过这个点(可以是到达)参考上图,3是割点,2是割点,4不是割点.


总结

其实tarjan算法最主要的就是一个DFN数组和一个LOW数组,对应着图上每个点的遍历顺序和每个点在DFS树中最往上能翻到哪个点.求出这两个数组,就可以用这两个数组求强连通分量,割点和桥.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值