Tarjan算法详解

最近刚刚学了 Tarjan 算法,于是就写了这篇博客
那么 Tarjan 算法有什么用呢?
对于一个图来说,它的性质其实是很少的,因此我们能对它进行的操作方式也极少
于是将其转化为一颗树(如果是有向图不一定是树)是一个非常重要的思路
Tarjan 算法则是用以进行这种转化的比较高效的算法
即用以进行无向图/有向图的联通分量缩点的过程
那么这就需要涉及到一些定义:



无向图:

割点:

若删掉某点 P 后,无向图G分裂为两个或两个以上的子图,则称 P G的割点

桥(割边):

若删掉某条边 B 后,无向图G分裂为两个或两个以上的子图,则称 B G的桥(割边)

点双连通图:

没有割点的图称为点双连通图

边双连通图:

没有割边的图称为边双连通图

双联通分量:

无向图 G 的极大【不是最大的意思,而是尽可能大】(点/边)双连通子图称为(点/边)双连通分量。

缩点:

即把一个连通分量缩为一个点的过程,就是删除该连通分量内所有的点和边,
然后新建一个点,向所有与连通分量中的点有边相连的点连边


有向图

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通

强连通图:

如果有向图 G 的每两个顶点都强连通,则称G是一个强连通图。

强连通分量:

有向图的极大强连通子图,称为强连通分量



接下来正式介绍 Tarjan 算法的流程
Tarjan 算法对于图中的每个节点引入了两个值:
dfn[x] dfs 序,储存节点 x dfs过程中到达的第几个节点
low[x] 储存节点 x 的子树不经过搜索树所能到达的dfn最小的点
而求取这两个值得过程即为:

void dfs(int x,int pre){
    dfn[x]=++tp;low[x]=tp;
    for(int i=head[x];~i;i=edge[i].nxt){
        int y=edge[i].to;
        if(y==pre)continue;
        if(dfn[y])low[x]=min(low[x],dfn[y]);//若节点y已被遍历过,那么用节点y的dfs序来更新x的low值
        else {
            dfs(y,x);
            low[x]=min(low[x],low[y]);//若节点y没有被遍历过,那么用节点y的low值来更新节点x的low值
            /*
                这一部分即为
            */
        }

    }
}
//自己领悟吧,有图
//每个小圆为节点,圆上编号代表dfn值,红色数字代表low值,深色为树边,其余为非树边

“`

那么这两个值有什么用呢?
有以下几种用法:


求割点:

根据割点的定义,我们知道当我们删除节点 x 后整个图若分为两个或以上的部分则该点为割点
那么该怎么判断这个点是不是割点呢?
我们知道,如果节点x的任一子节点 p 所代表的子树不能通过非树边到达节点x的祖先,
那么一旦删除节点 x ,该子树就会从原来的图上脱离,即节点x为割点
那么就好办了,即当节点 x 的任一子节点p满足 low[p]>=dfn[x] 时,节点 x 为割点
当然,这是x不为根节点的情况,
x 为根节点时,需要有两个子节点的low值大于它的 dfn 值才为割点(即根节点的度大于等于 2

求桥:

求桥类似于求割点
而判桥时不需要分别根节点和非根节点
对于任一节点x和它的一个子节点 p ,若满足low[p]>dfn[x]
则连接他们的这条边是一座桥

求边双联通分量:

求边双联通分量比较简单,找到桥后把所有的桥全部删掉,
这时候整张图就变成了一个个孤立的边双联通分量
显然,每个节点只会出现在一个边双联通分量中

求点双联通分量:

求点双联通分量时我们需要用到一个栈
每次 dfs 到某一个点时我们将这个点 push 到栈中
而当我们寻找到一个割点时,就代表在栈中它以后(包括它)的这些点组成了一个点双联通分量
(这个自行脑补,不好描述,但还是比较容易证明其正确性的)
所以当我们发现 low[p]>=dfn[x]
就将 p 以及p以后的点全部 pop 掉,然后加上节点 x 构成一个点双联通分量
因此对于非割点来说,只会在一个点双联通分量中出现
而对于割点来说,会出现在多个点双联通分量中
这就是点双联通的题一般难于边双联通的原因
PS:搞不懂的可以对着那张图研究一下)

求强联通分量:

这种题我还没做过,毕竟有向图的题本来就比较少。。
求强联通分量时,由于从每个点出发不一定能遍历整个图
于是我们需要从每一个未访问的点开始 dfs ,而后将 dfs 过程中访问到的点加入栈中
如果发现对于任一节点 x ,满足low[x]==dfn[x]
在栈中将 x 以及x以后的元素弹出,形成一个强连通分量
因为点与点之间的强连通具有传递性(若 1<>2 , 2<>3 ,则 1<>3
low[x] 表示与 x 强连通的dfn最小的点
所以当 low[x]==dfn[x] 时说明节点 x 之前的节点中已经没有与其属于同一个强连通分量的点了。
显而易见的是,每个节点只会出现在一个强联通分量中


以上就是Tarjan算法较为一般的应用,还是比较简单实用的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值