有向图的强连通分量

有向图的强连通分量


定义:

在有向图G中,如果任意两个不同的顶点相互可达,则称该有向图是强连通的。有向图G的极大强连通子图称为G的强连通分支。

有向图强连通分支的Tarjan算法:

void tarjan(u){
	dfn[u]=low[u]=++index;//初始的开始时间。定义int index=0;
//dfn[u]:u节点在DFS过程中的访问序号(也叫开始时间)。
//lou[u]:从u节点出发DFS过程中u下方节点所能到达的最早的节点的开始时间。
	stack.push(u);//u节点入栈
	for each(u,v) in E{//枚举每一条边
		if(v is not visted){
			tarjan(v);
			low[u]=min(low[u],low[v]);
//对于u节点的子节点v,从v出发进行的DFS结束回到u时,有上述操作。因为
//u可达v,v可达的最早的节点也是u可达的。 
		} 
		if(v in stack){
			low[u]=min(low[u],dfn[v]);
//节点u右边连到栈内的节点v,则由上述操作。表明目前发现u可达的最早的节点是v。 
		} 
	}
	if(dfn[u]==low[u]){//u是一个强连通分量的根。表明u可达的所有节点,都不能到达任何比u早的节点。 
		repeat
			v=stack.pop//出栈
			print v;
		until(u==v) 
	}//退栈,把整个强连通分量都弹出来 
}//复杂度O(E+V) 

如何理解从u出发的DFS全部结束回到u时,若dfn[u]=low[u],此时将栈中u及其上方的节点弹出就找到了一个强连通分量:

此时所有的节点分为了以下几类:
(1)还未访问过的节点(这类节点,显然从u不可达)
(2)栈中比u早的节点(在u下方)(这类节点,由u不可达,因为low[u]=dfn[u])
(3)栈中比u晚的节点(在u上方)(这类节点,由u可达)
(4)栈中的u
(5)曾经入栈(被访问过),又出栈的节点(这类节点不可达u)
如此,我们现在只需要证明第(3)类节点可不可达u,第(5)类节点不可达u:

第(5)类节点不可达u:
第(5)类节点还可以分为两类,即晚于u的节点和早于u的节点。
早于u的节点不可达u:因为如果可达u,那么这个节点此时应该还在栈中,在u的下面,如此便导致矛盾。
晚于u的节点不可达u:任取晚于u的节点x。x之所以已经被弹出栈,一定是因为最终low[x]=dfn[x],或在栈里,x位于某个节点y的上方(由y可达,且y满足条件:最终的low[y]=dfn[y])。因为y曾经出现在u的上方,所以y一定晚于u。因为low[x]不可能小于等于dfn[x](否则low[y]也会小于等于dfn[y],这和low[y]=dfn[y]矛盾),所以x到达不了u及比u早的节点。

第(3)类节点可达u:
若有此类节点x不可达u,则考虑最终的low[x]:low[x] < dfn[u] 不可能,否则有 low[u]<dfn[u],low[x] = dfn[u] x可达u
其他:寻找x所能到达的最早的,栈里面的节点y,则y必然比u晚。而且有 low[y] = dfn[y](若此条不成立,则x还能到达比y更早的节点,矛盾)。而若 low[y] =dfn[y],则y应该已经被弹出栈了,y上方的x当然也已经不再栈中,这和x是第3类节点矛盾。
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值