Tarjan在图论中的应用(二)——用Tarjan来求割点与割边

前言: T a r j a n Tarjan Tarjan

割点割边建立在 T a r j a n Tarjan Tarjan算法的基础之上,因此建议在看这篇博客之前先去学一学 T a r j a n Tarjan Tarjan

L i n k Link Link

T a r j a n Tarjan Tarjan算法详见博客Tarjan在图论中的应用(一)——用Tarjan来实现强连通分量缩点


回顾 T a r j a n Tarjan Tarjan中各个数组的定义

首先,我们来回顾一下 T a r j a n Tarjan Tarjan中各个数组的定义:

d f n [ dfn[ dfn[ ] ] ]:每个点的 d f s dfs dfs序。

l o w [ low[ low[ ] ] ]:每个点能到达的 d f s dfs dfs序最小的节点的 d f s dfs dfs序。

而其他数组在求割点和割边的过程中则不太必要了。


割点

首先,我们要了解一下割点的定义把这个点去掉之后,这个点所在的联通块就会被分成若干个联通块

既然这样,也就是说,只要这个节点某一个子节点所到达的节点的 d f s dfs dfs序大于等于该节点的 d f s dfs dfs序,即它的这个子节点无法到达 d f s dfs dfs序小于该节点的节点,就说明它是一个割点了。

而对于一个联通块第一个访问的节点,则需特判,如果它在遍历完一个节点所能遍历到的所有节点,还能找到没有被遍历过的节点,就说明它是一个割点。

代码如下:

inline void Tarjan(int x,int lst)//Tarjan求割点
{
	register int i,tot=0;//tot记录访问到的子节点个数
	for(dfn[x]=low[x]=++d,i=lnk[x];i;i=e[i].nxt)//枚举每一个子节点
	{
		if(!(e[i].to^lst)) continue;//如果这个节点是当前节点的父亲节点,就跳过
		if(!dfn[e[i].to])//如果这个子节点没有被访问过 
		{
			Tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]),++tot;//遍历该子节点,更新low[x],并将tot加1
			if(lst&&low[e[i].to]>=dfn[x]) IsCut[x]=1;//如果当前节点不是一个联通块第一个访问的节点,且当前节点的这个子节点dfs序大于等于当前节点的dfs序,那么就说明当前节点是割点
		}
		else low[x]=min(low[x],dfn[e[i].to]);//更新low[x]
	}
	if(!lst&&tot>=2) IsCut[x]=1;//如果当前节点是联通块第一个节点,且访问到的子节点个数≥2,那么就说明当前节点是割点
}

割边

还是一样,先了解一下割边的定义把这条边去掉之后,这条边所在的联通块就会被分成若干个联通块,看起来与割点的定义很像。

因此,如果一条边所连接的两个节点,若其中 d f s dfs dfs序较大的节点不经过这条边所能到达的 d f s dfs dfs序最小的节点大于这条边连接的点中 d f s dfs dfs序较小的节点,就说明这条边是一条割边。

不过还要注意判重的情况,要注意如果有两条相同的边,那么这两条边肯定都不是割边。

代码如下:

inline void Tarjan(int x,int lst)//Tarjan求割边
{
	register int i,flag=1;//flag记录当前节点是否第一次访问它的父亲节点
	for(dfn[x]=low[x]=++d,vis[Stack[++top]=x]=1,i=lnk[x];i;i=e[i].nxt)//枚举每一个子节点
	{
		if(flag&&!(e[i].to^lst)) {flag=0;continue;}//如果是第一次访问父亲节点,就将flag标记为0,并跳过这条边
		if(!dfn[e[i].to])//如果这个子节点没有被访问过
		{
			Tarjan(e[i].to,x),low[x]=min(low[x],low[e[i].to]);//访问这个节点,并更新low[x]
			if(low[e[i].to]>low[x]) IsBridge[i]=1;//如果这条边连向的另一个节点所能到达的dfs序最小的节点大于该节点的dfs序,就说明这条边是割边
		}
		else if(vis[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);//否则,更新low[x]
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值