Tarjan算法

Tarjan算法

Tarjan算法是一个很著名的算法,其主要的目的是用来求有向图的强连通分量,什么是强连通分量呢? 简单点说就是存在一个点 v 1 v_1 v1能到点 v 2 v_2 v2,且 v 2 v_2 v2也能到 v 1 v_1 v1,那么 v 1 , v 2 v_1, v_2 v1,v2就构成一个强连通分量; 具体的定义参见百度。

Tarjan算法步骤

Tarjan算法采用的深度优先遍历的方式(DFS),需要两个数组dfn[]和low[],dfn数组记录结点在dfs搜索下的访问顺序, low记录结点能能追溯的最早的祖先; 算法同时还需要一个栈来记录当前已经访问过的结点。

算法一开始指定一个结点为根结点,然后从这个结点用dfs进行遍历整个图。 每次遍历一个结点是记录结点的dfs序,刚开始low的值等于dfs的值,在回溯的过程我们更新low的值,更新过程为 l o w [ u ] = m i n ( l o w [ u ] , l o w [ v ] ) low[u]=min(low[u],low[v]) low[u]=min(low[u],low[v]), v v v u u u的子结点,若当前结点被访问过则判断其是否在栈中,若在栈中,则同时也按上面的步骤进行更新。若结点不在栈中,则其也被访问过,则说明结点 v v v无法到达结点 u u u.

用来判断是否为一个连通分量的标准为 d f n [ u ] = = l o w [ u ] dfn[u]==low[u] dfn[u]==low[u],此时从栈顶到结点u之间的结点为一个强连通分量. 因为每个结点记录的是它和它子结点所能追溯到的最早的祖先,再回溯过程中会不断更新每个结点能追溯到的最早访问的祖先,当遇到 d f n [ u ] = l o w [ u ] dfn[u]=low[u] dfn[u]=low[u]时,则表示这个点的 l o w [ u ] low[u] low[u]已经被其他结点更新过了,所有 l o w [ i ] = = l o w [ u ] low[i]==low[u] low[i]==low[u]的结点在同一个连通分量。然后只要对栈中的结点进行染色标记就可以找出连通分量了。

C++模版

写得不太好,欢迎指出错误。

/*
有向图的强连通分量-Tarjan算法;

*/

#include<bits/stdc++.h>
using namespaec std;
typedef long long ll;
const int maxn=1e5+5;
int n,m;
int dfn[maxn],low[maxn],vis[maxn],color[maxn],Stack[maxn];
int top,deep,sum;

inline int min(int x,int y){
	return x<y?x:y;
}

void Tarjan(int u){
	dfn[u]=++deep;		//记录结点的dfs序;
	low[u]=deep;		//记录结点最早能追溯的祖先;
	vis[u]=1;
	Stack[++top]=u;		//把结放入栈中;
	for(int i=0;i<v[u].size();i++){
		if(!dfn[v[u][i]]){		//判断结点是否被遍历过;
			Tarjan(v[u][i]);
			low[u]=min(low[u],low[v[u][i]]);
		}else{
			if(vis[v[u][i]]){		//判断结点是否栈了;
				low[u]=min(low[u],dfn[v[u][i]]);
			}
		}
	}
	if(dfn[u]==low[u]){		//找到一个强连通;
		color[u]=++sum;		//对连通分量内的点进行染色;
		vis[u]=0;
		while(Stack[top]!=u){
			color[Stack[top]]=sum;
			vis[Stack[top--]]=0;
		}
		top--;
	}
}

vector<int> v;
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	int x,y;
	while(m--){
		scanf("%d %d",&x,&y);		//x到y的有向边;
		v[x].push_back(y);
	}

	memset(vis,0,sizeof(vis));
	memset(dfn,0,sizeof(dfn));
	top=deep=sum=0;
	for(int i=0;i<n;i++){
		if(!dfn[i]){
			Tarjan(i);
		}
	}
	return 0;
}

新的开始,每天都要快乐哈!
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值