Tarjan的学习笔记 求割边求割点

本文详细介绍了Tarjan算法的原理,包括割点、割边的概念,以及如何使用时间戳进行在线性时间内求解割边。通过伪代码解释算法的具体步骤,并提供了针对树根的特判方法及处理有重边的图的技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

博主图论比较弱,搜了模版也不会用。。。

所以决心学习下tarjan算法。

割点和割边的概念不在赘述,tarjan能在线性时间复杂度内求出割边。

重要的概念:时间戟,就是一个全局变量clock记录访问结点的时间。一个无向图dfs会形成一个森林,当图只有一个连通分量时,就只有一棵树。

由于在无向图中,除了树边,其他都是反向边。可以画个图感受一下,可以反证的,如果有其他类型的边,那么dfs先沿着那些边跑图的,那么那些边就不存在。

如果结点是树根,那么它是割点的充要条件就是它有两个子结点。

定理

对于其他结点,如果他的子结点的反向边没有指向它的祖先的,那么它就是割点。证明很明显,因为无向图是没有横跨子树的边的。(对树根不成立哦~)

 

具体判断的时候借助时间戟,定义low(u)为u和其后代所能返回最早祖先的的dfn值,那么定理就可以等价的转化为low(v)>=pre(u)。而且如果v的后代只能返回自己,那么删除(u,v)的一条边就可以让图分连通,那么就找到了割边(桥)。

伪代码

int dfs(int u,int fa) 返回u的low值, fa是判断是不是树边的二次访问
{
  记录时间戟并初始化u的low值

  跑图{
  如果子节点v没访问过{
  dfs(v)并返回后代low值 
  用后代low值更新u的low值    
  如果 后代的low值>=pre       //根据要求的是割边还是割点替换判断条件

    那么u是割点           //用数组记录,因为一个割点,条件可能不只成立一次
  }否则 如果是反向边         // 一.要满足v的时间戟小于u的,二.v不是u的父节点(是无向图的边的二次访问)

     {
       用反向边更新u的low值 
     }
  }

  用数组记录low u

  返回 low u
}

对于树根可以特判,可以通过对代码的小改动来实现,做法是记录子结点数量child,初始调用时fa赋值-1,加一个判断fa<0且child == 1时iscut(u) = false

 这个不能跑重边

对于有重边的图可以采用以下技巧

如果是用前向星存正反两条边是相邻并且奇偶性一定是不一样的,那么可以利用异或的开关性,来判断是不是树边if(i==(id^1))continue;//不从i对应的边到父节点  

void tarjan(int u,int fa)
{
    dfn[u] = low[u] = ++clock;
    for(int i = head[i]; ~i ; i = nxt[i]){
        int v = to[i];
        if(!dfn[v]){
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v] > dfn[u]){
                ans = min(ans,wei[i])
            }
        }else if(v != fa) {
            low[u] = min(low[u],dfn[v]);
        }
    }
}

 如果从树根出发的话,那么有两个以上的结点,反而不是割边。(具体看想要连通哪里)

转载于:https://www.cnblogs.com/jerryRey/p/4659881.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值