浅谈 Tarjan(强连通分量算法)

前言

T a r j a n \tt Tarjan Tarjan 的题,教练给的课件实在是太La Ji了,于是重新整理一下 T a r j a n \tt Tarjan Tarjan 的思路

Tarjan

T a r j a n \tt Tarjan Tarjan算法,用于解决有向图的强联通分量问题。(当然不只是,这里是这样的)

在有向图中,若两点任意可达,则两点之间强联通。若有向图任意两点强联通,则称为强连通图。非强联通图的极大强联通子图称作强联通分量 T a r j a n \tt Tarjan Tarjan 算法就是用于找有向图的强联通分量。

首先,有三个概念需要了解:

  • 时间戳:即 d f s dfs dfs序: d f n dfn dfn
  • 搜索树:从某个节点出发访问,每个节点只被第一次访问,那么搜索到的节点由搜索边形成的一棵树,称作搜索树。
  • 追溯值:记为 l o w [ x ] low[x] low[x],表示以 x x x 作为搜索树的树根出发,能访问到的 d f s dfs dfs 序最小的点。这里能访问到的并不只是搜索树上的节点,还包括能通过某条非搜索树边到达搜索树上的节点。

那么我们用一个栈来维护这棵搜索树。对于图上的环,当访问到某个在栈内的节点时,即形成了环。

当搜完一个没被访问过的节点,更新 l o w [ x ] : l o w [ x ] = m i n ( l o w [ x ] , l o w [ t o ] ) low[x]:low[x]=min(low[x],low[to]) low[x]:low[x]=min(low[x],low[to])
当搜到一个在栈内的节点,有环了,那么这是将这个点作为强联通分量的根(抽象理解即可)。更新 l o w [ x ] = m i n ( l o w [ x ] , d f n [ t o ] ) low[x]=min(low[x],dfn[to]) low[x]=min(low[x],dfn[to])

当搜索完一个节点,且 d f n [ x ] = l o w [ x ] dfn[x]=low[x] dfn[x]=low[x],那么这个时候这里就形成了一个强联通分量。退栈,用 v e c t o r \tt vector vector 记录下这个强联通分量即可。

感性理解为: d f n dfn dfn 是一些向下的边, l o w [ x ] low[x] low[x]是一条返回环的边,这个时候形成了一个联通分量。若 d f n [ x ] = = l o w [ x ] dfn[x]==low[x] dfn[x]==low[x],则找到了一个最大环,也就是一个强联通分量。

Code:

void Tarjan(LL x)
{
	S.push( x );
	InStack[x] = 1; 
	Dfn[x] = Low[x] = ++ Cnt;
	int l = G[x].size();
	for (Int i = 0; i < l; ++ i)
	{
		LL to = G[x][i];
		if (! Dfn[to])
		{
			Tarjan( to );
			Low[x] = Min(Low[x], Low[to]);
		}
		else if ( InStack[to] )
			Low[x] = Min(Low[x], Dfn[to]);
	}
	if (Dfn[x] == Low[x])
	{
		LL u;
		do
		{
			u = S.top();
			S.pop();
			InStack[u] = 0;
		} while(u != x);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值