Tarjan算法三大应用之双连通分量

10 篇文章 0 订阅
9 篇文章 0 订阅

基本概念

  • 定义1:

    割点集合:点集 VV ,若 G 删除了V后不连通,但删除了 V 的任意真子集后 G 仍然连通,则称V为割点集合

    割点:若某一结点就构成了割点集合,那么称此结点为割点关节点

    点连通度:点数最少的割点集合

    割边集合:边集 EE ,若 G 删除了E后不连通,但删除了 E 的任意真子集后 G 仍然连通,则称E为割边集合

    割边:若某一结点就构成了割边集合,那么称此边为割边关节边

    边连通度:边数最少的割边集合

    块:没有割点的极大连通子图

  • 定义2:

    如果一个无向连通图的点连通度大于1,则称该图是点双连通的,简称双连通。通俗来说就是不存在割点。

    如果一个无向连通图的边连通度大于1,则称该图是边双连通的,也简称双连通。通俗来说就是不存在割边。

    可以看出,点双连通与边双连通都可以简称为双连通,它们之间是有着某种联系的。(双连通图一定既是点双连通的又是边双连通的)

  • 定义3:

    在图 G 的所有子图G中,如果 G 是双连通的,则称 G 双连通子图。如果一个双连通子图 G 它不是任何一个双连通子图的真子集,则 G 极大双连通子图

    双连通分量,就是图的极大双连通子图。特殊的,点双连通分支又叫做

tarjan算法求割点和桥

tarjan算法求解双连通分量的做法和求解有向图的强连通分量做法类似。

  • 点双连通分量

    求点双连通分量也就是去找割点

    在dfs搜索树中,如果点k满足下列两个条件之一,那么k是割点:

    1. k 为深搜树的根,且 k 的儿子个数 2
    2. k 为深搜树的中间节点(k 既不是根也不是叶),且 low[son]>=dfn[k]

    第一个条件很好理解,第二个条件son表示k的儿子,这句话的意思也就是说k的子孙中不会有某个点有追溯到k的祖先的边。

    这样,对于求点连通分量算法中low值的严格定义将更新为:

    low(u)=Min
    {
    ​ dfn(u)
    ​ low[v] v是u 的一个儿子
    ​ dfn[v] v与u邻接,且边(u,v)是一条返祖边
    }

    算法具体细节见代码:

    void tarjan(int p,int u)
    {
        dfn[u] = low[u] = ++ti; // 时间戳
        int son = 0; // 当前节点儿子的个数
        for (k=head[u]; k!=-1; k=edge[k].next) //前向星实现
        {
            int v = edge[k].to;
            if (v == p)
                continue;
            if (dfn[v] == 0)
            {
                son++;
                tarjan(u,v);
                if (low[v] < low[u])
                    low[u] = low[v];
                if((u!=1 && dfn[u]<=low[v]) || (u==1&&son>=2))
                    istcc[u]=1; // 条件符合,u是割点
            }
            else
                low[u] = min(low[u],dfn[v]);
        }
    }
  • 边双连通分量

    求边双连通分量也就是去找割边

    割边的求解过程和求割点的方法类似,判断方法是:

    无向图中的一条边(u,v)是割边,当且仅当(u,v)是生成树中的边,且满足 dfn[u]<low[v]

    找到一条割边就将割边下面的边连通分量出栈。

    stack<int> St;
    void Tarjan(int p,int u)
    {
          dfn[u]=low[u]=++ti;
          St.push(u);
          vis[u]=1;
          for(int k=head[u];k!=-1;k=edge[k].next)
          {
                int v=edge[k].v;
                if(v==p)continue;
                if(!dfn[v])
                {
                    Tarjan(u,v);
                    low[u]=min(low[u],low[v]);
                    if(low[v] > dfn[u]){  
                        cnt_bri++;//双联通分量块的个数,多一个桥即多一个双联通分量  
                }
                else// if(vis[v]==1)//v在栈中,说明(u,v)是返祖边
                {
                    low[u]=min(low[u],dfn[v]);
                }
            }
          if(dfn[u]==low[u])//将u所在的边连通分量出栈
          {
               int x;
               ++cnt;//cnt表示缩点后的连通分量
               while(1)
               {
                     x=St.top();
                     St.pop();
                     vis[x]=0;
                     belong[x]=cnt;
                     if(x==u)break;
               }
          }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值