tarjan算法浅谈

1.求割点: 
割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点。 
原理:若low[v]>=dfn[u],则u为割点。因low[v]>=dfn[u],则说明v通过子孙无法到达u的祖先。那么对于原图,去掉u后,必然会分成两个子图。 

所以处理节点u时,先递归v的子节点,然后回溯至u时,如果满足low[v]>=dfn[u],则u为割点。

代码:

int dfn[maxn], low[maxn];
int gcnt, iscut[maxn];
struct Edge{
    int v, next;
}E[maxn * maxn];
int head[maxn], tol;
 
void Init(){
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(iscut, 0, sizeof iscut);
    gcnt = tol = 0;
}
 
void add_edge(int u, int v){
    E[tol].v = v;
    E[tol].next = head[u];
    head[u] = tol++;
}
 
void Tarjan(int u, int pre){
    dfn[u] = low[u] = ++gcnt;
    int son = 0;
    for(int i = head[u]; ~i; i = E[i].next){
        int v = E[i].v;
        if(v == pre) continue;
        if(!dfn[v]){
            son++;
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if(u != pre && low[v] >= dfn[u]) iscut[u] = 1;
        }else low[u] = min(low[u], dfn[v]);
    }
    if(u == pre && son > 1) iscut[u] = 1;
}
2.求桥 
桥(割边):删掉它之后,图必然会分裂为两个或两个以上的子图。 
原理:若low[v]>dfn[u],则(u,v)为桥。
(无向图)代码:

int dfn[maxn], low[maxn];
int gcnt, iscut[maxm];
struct Edge{
    int v, next;
}E[maxm];
int head[maxn], tol;
 
void Init(){
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(iscut, 0, sizeof iscut);
    gcnt = tol = 0;
}
 
void add_edge(int u, int v){
    E[tol].v = v;
    E[tol].next = head[u];
    head[u] = tol++;
}
 
void Tarjan(int u, int pre){
    dfn[u] = low[u] = ++gcnt;
    for(int i = head[u]; ~i; i = E[i].next){
        int v = E[i].v;
        if(i == (pre ^ 1)) continue;//因为无向图简图用双向边,此处是消除双向边的影响
        if(!dfn[v]){
            Tarjan(v, i);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u]) iscut[i] = iscut[i ^ 1] = 1;
        }else low[u] = min(low[u], dfn[v]);
    }
}

(无向图)综合代码:

int Vcnt=0;
int Ecnt=0;
dfs(int u,int father,int depth) //u是当前结点,father父节点,depth记录dfs标号
{
    visit[u]=1; 
	low[u]=dfn[u]=depth;
    int child=0;//记录u的孩子数 
	
	for (int i=0;i<G[u].size();i++)
	{  
	    int v=G[u][i];
		if (visit[v]==1&&v!=father)
			low[u]=min(low[u],dep[v]);
		if (visit[v]==0)
        {
        	child++;
			dfs(v,u,depth+1);
			low[u] = min(low[u], low[v]);
            
            if(u==root&&child>1||u!=root&&low[v]>=dfn[u])
            {
            	cutpoint[u]=1;//是割点
				Vcnt++; 
			}

			if (low[v] > dfn[u])
            {
            	//<u,v>是割边;
				Ecnt++; 
			}
	    }
	}
	visit[u]=2;
}
3.点双连通分量 边双连通分量

点双连通图的定义等价于任意两条边都同在一个简单环中,而边双连通图的定义等价于任意一条边至少在一个简单环中。对一个无向图,点双连通的极大子图称为点双连通分量(简称双连通分量),边双连通的极大子图称为边双连通分量

4.有向图强连通分量 无向图连通分量

(有向图)代码:

int dfn[maxn], low[maxn];
int sta[maxn], top, gcnt, sub;
int id[maxn], inS[maxn];
int ind[maxn], outd[maxn];
struct Edge{
    int v, next;
}E[maxn * maxn];
int head[maxn], cnt;
 
void Init(){
    memset(head, -1, sizeof head);
    memset(dfn, 0, sizeof dfn);
    memset(inS, 0, sizeof inS);
    gcnt = cnt = top = scc = 0;
}
 
void add_edge(int u, int v){
    E[cnt].v = v;
    E[cnt].next = head[u];
    head[u] = cnt++;
}
 
void Tarjan(int u){
    dfn[u] = low[u] = ++gcnt;
    sta[++top] = u;
    inS[u] = 1;
    for(int i = head[u]; ~i; i = E[i].next){
        int v = E[i].v;
        if(!dfn[v]){
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }else if(inS[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(dfn[u] == low[u]){
        scc++;
        while(1)//记录每一个点属于的连通块
        {
            
            int v=sta[top--];
            id[v] = scc;
            inS[v] = 0;
            if(v==u)
                break;
         }
    }
}
(无向图)代码:

struct Edge{
    int v, next;
}E[maxm << 1];
int head[maxn], tol;
int dfn[maxn], low[maxn], id[maxn], sta[maxn], inS[maxn],top, cnt, scc;
 
void init(){
    memset(head, -1, sizeof head);
    tol = cnt = top = sub = 0;
    memset(dfn, 0, sizeof dfn);
    memset(inS, 0, sizeof inS);
}
 
void add_edge(int u, int v){
    E[tol].v = v;
    E[tol].next = head[u];
    head[u] = tol++;
}
 
void Tarjan(int u, int pre){
    dfn[u] = low[u] = ++cnt;
    sta[++top] = u;
    inS[u] = 1;
    for(int i = head[u]; ~i; i = E[i].next){
        if(i == (pre ^ 1)) continue;
        int v = E[i].v;
        if(!dfn[v]){
            Tarjan(v, i);
            low[u] = min(low[u], low[v]);
        }else if(inS[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        int v;
        sub++;
        do{
            id[v = S[top--]] = scc;
            inS[v] = 0;
 
        }while(u != v);
    }
}

for(int i = 1; i <= n; i++){
        if(!dfn[i]) Tarjan(i, -1);
    }


猜你喜欢

转载自blog.csdn.net/m0_37846371/article/details/77993829
今日推荐