有向图的强连通分量(Tarjian)

本文详细解析了有向图中的强连通分量概念,以及如何使用Tarjan算法高效地寻找这些分量。通过理解强连通分量,我们可以更好地分析和处理有向图中的连接性问题。

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

强连通分量


有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。
在这里插入图片描述

Tarjian算法

对于一个强连通分量 SCCSCCSCC,从任何一个点出发都能遍历整个图。假设 SCCSCCSCC 第一个发现的点为 xxx ,那么在访问完 xxx 的所有子节点直接输出就是这个 SCCSCCSCC,可是我们不知道这个点是不是 SCCSCCSCC 第一个被发现的点,

问题转化为判断一个点是否为 SCCSCCSCC 中最先被发现的点。

假设当前的点为 uuu
如果从 uuu 的子节点可以到达 uuu 的祖先节点,那么 uuu 肯定不是他所在的 SCCSCCSCC 第一个被发现的点。如果从 uuu 的子节点最多只能到达 uuu ,那么 uuu 肯定是第一个点。

之前求无向图割顶和桥的问题时,引入过 lowlowlow 数组,同样的需要 lowlowlow 数组。


vector<int> G[maxn];
int pre[maxn], low[maxn], sccno[maxn], dfs_clock, scc_cnt;
stack<int> s;

void dfs(int u){
    pre[u] = low[u] = ++dfs_clock;          // 标记每个点访问时间
    s.push(u);
    for (int i = 0; i < G[u].size(); i++){  // 遍历 u 所有子节点
        int v = G[u][i];
        if(!pre[v]){
            dfs(v);
            low[u] = min(low[u], low[v]);    // 用子节点的 low 值更新 u 的 low 值。
        }else if(!sccno[v])                  // 如果 v 不属于 SCC
            low[u] = min(low[u], pre[v]);    // 反向边更新 u 的 low 值
    }
    if(low[u] == pre[u]){                   // 如果 u 及其后代最早能达到的祖先只能到 u
        scc_cnt++;                          // u 就是 SCC 访问第一个点
        while(1){
            int x = s.top();
            s.pop();
            sccno[x] = scc_cnt;
            if(x == u)
                break;
        }
    }
}


void find_scc(int n){
    dfs_clock = scc_cnt = 0;
    memset(sccno, 0, sizeof sccno);
    memset(pre, 0, sizeof pre);
    for(int i = 0; i < n; i++){
        if(!pre[i])
            dfs(i);
    }
}

程序用一个栈保存当前 SCC 中的左右节点,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值