• 连通分量(tarjan算法)


    对于有向图中,连通分量叫强连通分量

    对于无向图中,连通分量叫双连通分量,而在双连通分量中,又分为点双连通和边双连通。

    重点讨论双连通的情况:

    以割点区分连通情况的双连通叫做点双连通分量,以割边区分连通情况的双连通叫做边双连通分量。

    比如这个图中:

    1           4

    | \       /   \ 

    |   \   /       \

    |     2          5

    |   /   \       /

    | /       \   /

    3          6

    如果要的到两个连通分量的话,我们只有以2节点为割点,将图分为点双连通。我们便可以得到两个双连通分量(1,2,3)、(2,4,5,6)。

    再比如这个图:

    1                5

    | \             /   \ 

    |   \         /       \

    |     2 — 4       6

    |   /         \       /

    | /             \   /

    3                7

    如果要得到两个连通分量的话,我们只能以(2,4)边为割边,得到两个边双连通分量。

    具体是将一个无向图分成点双连通还是边双连通得看具体的问题。下面具体讨论一下用tarjan算法实现起来的区别。

    1、对于边双连通分量,由于是以边为连通间的区分,那么每个点只属于一个连通分量,我们在深度遍历的时候,只需要记录某一点是否被访问即可。

    2、但是对于点双连通分量,由上图也可以看出,2节点同时属于两个连通分量,如果还是以节点为重判对象的话,将得到错误的答案。此时,我们应该以边为重判对象,而该边的两个端点,都同属于一个点双连通。

    对于边双连通的实现,由于没有节点会重复属于两个以上的连通分量,所以我们可以先简单的遍历一遍整个图的tarjan标号(low、dnf)。之后再进行一次深度遍历,以low[v]>dnf[u]为换色标准染色,最后每种颜色即一个分量。

    代码:

    void tarjan(int u,int f) //f为父节点
    {
    	low[u]=dnf[u]=++count;
    	visit[u]=1;
    	S.push(u);
    	for(node *p=link[u];p;p=p->next)
    	{
    		if(p->v==f)
    			continue;
    		if(!visit[p->v]) //一节点为重判对象
    		{
    			tarjan(p->v,u);
    			if(low[u]>low[p->v])
    			{
    				low[u]=low[p->v];
    			}
    		}
    		else if(low[u]>dnf[p->v])
    			low[u]=dnf[p->v];
    	}
    }
    
    void set_color(int u)
    {
    	for(node *p=link[u];p;p=p->next)
    	{
    		if(!color[p->v])
    		{
    			if(low[p->v]>dnf[u]) //找到了关键割边,换一种颜色染色
    				color[p->v]=++cut;
    			else
    				color[p->v]=color[u]; //保持原来的颜色
    			set_color(p->v);
    		}
    	}
    }
    

    对于点双连通的实现,经过上面的分析,由于存在点既属于A分量又属于B分量,所有我们此时采纳边为重判对象,而该边的两端点同属于一个分量

    代码:

    void col(int u)
    {
    	int x;node *p;
                 ++cut;  
    	do
    	{
    		p=stack[--top];
    		color[p->v]=color[p->op->v]=cut;
    	}while(p->op->v!=u);
    }
    
    void tarjan(int u)
    {
    	low[u]=dnf[u]=++cut;
    	for(node *p=link[u];p;p=p->next)
    	{
    		if(p->vist) //该边已被访问
    			continue;
    		p->vist=p->op->vist=1;  //两个方向都标记已访问
    		stack[top++]=p;   //该边进栈
    		if(!dnf[p->v])
    		{
    			tarjan(p->v);
    			if(low[u]>low[p->v])
    				low[u]=low[p->v];
    			if(low[p->v]>=dnf[u]) //找到了关键割点
    				col(u);
    		}
    		else if(low[u]>dnf[p->v])
    			low[u]=dnf[p->v];
    	}
    }
    
     
  • 相关阅读:
    Java知识系统回顾整理01基础04操作符02关系操作符
    Java知识系统回顾整理01基础04操作符01算术操作符
    Java知识系统回顾整理01基础03变量09块
    Java知识系统回顾整理01基础03变量08表达式
    Java知识系统回顾整理01基础03变量07final关键字
    Java知识系统回顾整理01基础03变量06变量的作用域
    Java知识系统回顾整理01基础03变量05变量命名规则
    Java知识系统回顾整理01基础03变量04类型转换
    leetcode-----64. 最小路径和
    leetcode-----63. 不同路径 II
  • 原文地址:https://www.cnblogs.com/ka200812/p/2126108.html
Copyright © 2020-2023  润新知