Tarjan算法

(本人码字困难户)


Tarjan


前几天联训刚刚学习了 Tarjan 算法;


处女作,第一次鼓起勇气厚着脸皮post csdn,可能有些理解不透彻,希望各位大神指点江山。


顺便说一句,有一些图论的题需要我们去求解环的问题,而此时就会有两种算法供我们选择 ; 一种是 Tarjan ,一种是阿三算法—— Kasoraju ; 他们都是通过dfs实现的 ;

Kasoraju就不写了,看看Tarjan ;

tarjan算法适用于求强连通分量,所以它本身板子的只能用于求有向图的强连通分量;对于强连通分量,可以感性的认为它是一个环;


下面是自己写的 Tarjan 算法:


#include<bits/stdc++.h>
using namespace std;
const int N=10000+5;
int g[N][N],dfn[N],low[N],s[N],cn,t,n,x,y,m,k;
bool ins[N];
void tarjan(int x){
	int j;
	cn++;
	dfn[x]=low[x]=cn;
	t++;s[t]=x;ins[x]=true;
	for(int i=1;i<=g[x][0];i++){
		j=g[x][i];
		if(!dfn[j]){
			tarjan(j);
			low[x]=min(low[x],low[j]);
		}
		else if(ins[j]) low[x]=min(low[x],dfn[j]);
	}
	if(dfn[x]==low[x]){
		cout<<"强连通分量 #"<<++k<<" : ";
		while(s[t]!=x){
			ins[t]=false;
			cout<<s[t]<<" ";
			t--;
		}
		ins[x]=false;
		t--;
		cout<<x<<endl;
	}
	return ;
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		g[x][++g[x][0]]=y;
	}
	tarjan(1);
	return 0;
}



对于g[x][i] , 表示第x个点的第i个出点;

对于m,代表边数;

对于dfn[i],表示第i个点进栈的时间(被dfs到的时间),也就是众人所说的时间戳;

对于low[i],表示第i个点进过一条非树边能够到达的点的时间戳;

对于s[t]=x,表示的是栈顶的元素是x;

对于ins[x]数组,表示的是元素x在不在栈中;


解释一下low数组的意义:


想象一下我们深陷迷雾之中;

从一个点开始走着,走着,并且我们只能向前看...

规定假如这两个点连通,那么我们站在该点能看见那另一个点;否则看不到;


每向前走一步如果我们可以看见原来走过的点,那么这显然就是一个环,不是吗?


这个low值记录的就是能够看到的走过最早的点;

显然我们可以看到我们正站着的点,所以low的初值是该点dfn的值;(当然不幸的是我正在迷雾中寻找着那个走过的点,可从来回不去...)


最后是弹栈的过程:

if(dfn[x]==low[x]){
	cout<<"强连通分量 #"<<++k<<" : ";
	while(s[t]!=x){
		ins[t]=false;
		cout<<s[t]<<" ";
		t--;
	}
	ins[x]=false;
	t--;
	cout<<x<<endl;
}


首先如果我在当前点除了先前看看不到前缀点了,表示我本身或者与我连通的点是强连通分量(dfn[i]==low[i])!


栈会控制该点是否是单独的强连通分量(当它是栈顶元素时).因为如果它有后缀结点,后缀结点却没有出栈,表明后缀结点的dfn与low一定不同,而它后面那个结点(Y)的low值一定是小于等于它(Y)的dfn值的,但是它(Y结点)没有弹栈,说明它的dfn值 与 我当前所在节点(令其名曰:T)的low值的关系一定是:

dfn [ T ] >= low[ Y ]

说明我当前节点一定在某一个大小大于1的强连通分量中!


但是可能你会问:前面的点怎么弹呢?

首先我们可以确定,如果它前面还有强连通分量中的点,它是不会被弹栈的,因为它的dfn值一定不等于low值;


当然只是纸上谈兵;捧上几个栗子;


test#1

5 6
1 2
2 3
3 4
4 5
5 3
3 2


test#2

5 6
1 2
2 3
3 4
4 5
5 3
3 2


如果有什么特殊情况,或是有错误的,欢迎来捶我的胸口qwq;

相信你们也不怎么想捶qwq;


感谢Nationalnight的精彩传授;

ps:推荐读者去看一下wans的博客,别忘了给她留个赞,谢谢;

  • 1
    点赞
  • 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、付费专栏及课程。

余额充值