Tarjan算法小结

无向图

相关概念:割点、桥、点、(点/边)双连通分量。

  • 若边 ( x , y ) (x,y) 为桥,则 d f n [ x ] < l o w [ y ] dfn[x]<low[y] ,注意父亲来的边
void tarjan(int x,int E) {
	dfn[x]=low[x]=++num;
	for(int k=last[x];k;k=a[k].next) {
		int y=a[k].y;
		if(!dfn[y]) {
			tarjan(y,k^1);
			low[x]=min(low[x],low[y]);
			if(dfn[x]<low[y])
				bridge[k]=bridge[k^1]=1;
		}
		else if(k!=E) low[x]=min(low[x],dfn[y]);
	}
}
  • x x 为割点,则 d f n [ x ] < l o w [ y ] , y s o n [ x ] dfn[x]<low[y],y\in son[x] 。特别的,对于根节点,应该有两个以上这样的子节点。
void tarjan(int x) {
	low[x]=dfn[x]=++num;
	int flag=0;
	for(int k=last[x];k;k=a[k].next) {
		int y=a[k].y;
		if(!dfn[y]) {
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(dfn[x]<=low[y]) {
				flag++;
				if(x!=root || flag>1) cut[x]=1;
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}
			 
  • 边双连通分量的求法:把所有桥标记,(桥数+原连通块个数=点双连通分量个数)不走桥来标记各个连通块,就找出了所有 e D C C e-DCC .用桥边连接各个 e D C C e-DCC ,从而实现缩点。
  • 点双连通分量的求法:1.第一次访问节点时,让它入栈。2.当出现 d f n [ x ] < = l o w [ y ] dfn[x]<=low[y] 时,弹出节点,直至 y y 被弹出。弹出的点和 x x 构成一个 v D C C v-DCC .
    点双连通分量的缩点:用割点连接各个 v D C C v-DCC .
    点双连通分量的求法的正确性证明:
    根据栈先进后出的性质,对于一个节点 x x ,它在 l o w [ x ] low[x] 时会被弹出——显然这构成一个简单环。
    如何理解呢?
    在这里插入图片描述

有向图

用一个栈存有后向边到达当前点祖先的点.
这些点一定是对求环有用的.
所以可以得到如下算法:

void tarjan(int x) {
	dfn[x]=low[x]=++num;
	v[sta[++top]=x]=1;
	for(int k=last[x],y;k;k=a[k].next) {
		y=a[k].y;
		if(!dfn[y]) {
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(v[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x]) {
		int y; ++cnt;
		do {
			v[y=sta[top--]]=0;
			dcc[y]=cnt;
		} while(y^x);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/104407161