首页 > 技术文章 > Tarjan强连通分量 学习笔记

jiangtaizhe001 2021-05-27 19:09 原文

定义

什么是强连通分量呢?
在有向图中,如果两个顶点能互相到达另一个点,则称两个顶点强连通。如果有向图的每两个顶点都强连通,称这个图是一个强连通图。有向图的极大强连通子图,称为强连通分量。
其实就是一个图中的一个极大的子图,这个子图里面的任何一个点都能到达其他的点。

算法实现

那么我们怎么求一个图的强连通分量呢?
我会暴力!
这里介绍一种线性算法内求出这个图的所有的强连通分量,这个算法由 Robert Tarjan 发明。

算法流程

定义 \(\operatorname{dfn}_i\) 为第 \(i\) 个点被搜到的顺序, \(\operatorname{low}_i\) 为第 \(i\) 个点能到达的点的最小的 \(\operatorname{dfn}\)
伪代码大概长这样:

void dfs(当前点){
    元素入栈;
    for( 枚举当前点可以到达的所有点 ){
        if(搜到的点没有访问过) dfs(搜到的点);
        if(搜到的点在栈中) 更新当前点的low;
    }
    if(当前点的low和dfn相等){
        弹出这个元素和它之上的元素;
        //弹出的元素属于同一个强连通分量之内
    }
}

证明:有什么好证明的,感性理解一下其实是显然的啊

代码实现

int n,m,u,v;
int head[maxn],nex[maxm],to[maxm],kkk;
#define add(x,y) to[++kkk]=y;\
nex[kkk]=head[x];\
head[x]=kkk
//链式前向星
int vis[maxn],dfn[maxn],low[maxn],cnt;
//vis代表这个点是否在栈中,cnt代表访问的点的个数
int scc[maxn],num;
//scc[i]代表i号点在第几个强连通分量内,num代表当前强连通分量的个数
typedef int type;
struct Stack{
	type data[maxn];
	int _top;
	void clear(){ _top=0; return; }
	void push(type x){ data[++_top]=x; return; }
	type top(){ return data[_top]; }
	void pop(){ _top--; return; }
	bool empty(){ return _top<=0; }
}s;//手写栈
void dfs(int x){
	s.push(x);
	vis[x]=1; low[x]=dfn[x]=++cnt;
	for(int i=head[x];i;i=nex[i]){
		if(!dfn[to[i]])
	    	dfs(to[i]);
	    if(vis[to[i]])
		    low[x]=min(low[x],low[to[i]]);
	}
	if(low[x]==dfn[x]){
		scc[x]=++num; vis[x]=0;
		while(s.top()!=x){
			scc[s.top()]=num;
			vis[s.top()]=0;
			s.pop();
		}
		s.pop();
	}
	return;
}//dfs
void tarjan(){
	s.clear(); num=0;
	for(int i=1;i<=n;i++)
	    if(!dfn[i])
	        dfs(i);
	s.clear();
	return;
}

推荐阅读