连通分量、强连通分量与Tarjan算法

本文详细介绍了如何通过Tarjan算法识别有向图中的强连通分量,包括其定义、在缩点转换中的作用,并展示了算法核心步骤和代码实现。理解强连通分量有助于理解图论在数据结构中的应用,以及如何构建无环图DAG。
摘要由CSDN通过智能技术生成

【定义】

对于一个有向图 G G G,其连通分量为:对于分量中任意两点 u , v u,v u,v,必然可以从 u u u走到 v v v,且从 v v v走到 u u u

强连通分量就是极大连通分量。即一个连通分量加上任何一些点之后它都不是一个连通分量,那么就把它称为强连通分量。

求强连通分量的意义在于能够把任意一个有向图经过缩点之后转化成一个有向无环图(DAG图),缩点是指将所有强连通分量缩成一个点。

【Tarjan算法】

首先我们按DFS序遍历一个图,那么这个图中的所有边可以分为四种:

在这里插入图片描述

那么某个点如果在强连通分量中,有以下两种情况:

  1. 存在后向边指向其祖先节点;
  2. 经过某个横叉边先走到了点 y y y,然后点 y y y存在前向边走到了某个祖先节点。

在Tarjan算法中我们需要引入一个时间戳的概念,我们按照DFS遍历的顺序给每个结点一个编号,假设为 1 ∼ N 1\sim N 1N,那么对于后向边 ( x , y ) (x,y) (x,y) y y y的时间戳一定小于 x x x,横叉边也是如此。

对于每个点,我们定义两个时间戳:

  • d f n [ u ] dfn[u] dfn[u]:表示第一次遍历到 u u u时的时间戳;
  • l o w [ u ] low[u] low[u]:表示从点 u u u开始走所能遍历到的最小的时间戳。

那么如果有 d f n [ u ] = = l o w [ u ] dfn[u]==low[u] dfn[u]==low[u],说明点 u u u是其所在的强连通分量的最高点。那么我们就可以把 u u u所在的这个强连通分量找出来。

【Tarjan算法代码框架】

void tarjan(int u)
{
    dfn[u] = low[u] = ++timestamp;//刚遍历到的时候先初始化当前点的时间戳
    stk.push(u), in_stk[u] = true;//将当前点加到栈中,然后标记这个点在栈中
    for (int i = h[u]; ~i; i = ne[i])//然后遍历u能到的所有邻点
    {
        int j = e[i];
        if (!dfn[j])//如果当前这个点还没被遍历过
        {
            tarjan(j);//遍历一下
            low[u] = min(low[u], low[j]);//儿子能到的最小时间戳u也能到
        }
        else if (in_stk[j])//如果当前这个点还是在栈中
            low[u] = min(low[u], dfn[j]);//那么就用当前点的时间戳更新一下u的low值
    }
    if (dfn[u] == low[u])//如果u为某个强连通分量的最高点
    {
        int t;
        scc_cnt++;//强连通分量的数量
        do
        {
            t = stk.top();
            stk.pop();
            in_stk[t] = false;
            id[t] = scc_cnt;//标记这个点是属于哪个强连通分量
        } while (t != u);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柃歌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值