当前位置:首页 > 技术 > 正文内容

Tarjan算法详解

访客 技术 2026年6月13日 1

Tarjan算法基础

Tarjan算法是一种经典的图论算法,主要用于解决有向图的强连通分量(SCC)以及无向图的双连通分量问题。本文将详细讲解其原理及实现。

有向图的强连通分量

在一个有向图中,如果任意两点之间都能互相到达,则称这部分为一个强连通分量。

基本概念

  • 强连通:有向图 G 中任意两个节点互达。
  • 强连通分量:极大强连通子图。
  • 连通分量:所有节点间可互相到达的集合。

时间复杂度为 O(n + m),其中 n 为点数,m 为边数。

算法逻辑

Tarjan 算法通过深度优先搜索(DFS)来遍历图,并利用两个数组记录每个节点的状态:

  • dfn[u]: 节点 u 的访问顺序。
  • low[u]: 节点 u 能回溯到的最小访问顺序。

同时使用栈来存储当前可能属于同一个强连通分量的节点。

代码实现


#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int dfn[N], low[N], scc_id[N], scc_size[N];
bool in_stack[N];
vector<int> adj[N];
stack<int> stk;
int timestamp, scc_count;

void tarjan(int u) {
    dfn[u] = low[u] = ++timestamp;
    stk.push(u);
    in_stack[u] = true;

    for (auto &v : adj[u]) {
        if (!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        } else if (in_stack[v]) {
            low[u] = min(low[u], dfn[v]);
        }
    }

    if (low[u] == dfn[u]) {
        ++scc_count;
        while (true) {
            int v = stk.top(); stk.pop();
            in_stack[v] = false;
            scc_id[v] = scc_count;
            scc_size[scc_count]++;
            if (v == u) break;
        }
    }
}

无向图的双连通分量

无向图的双连通分量分为两种:

  • E-DCC:边双连通分量。
  • V-DCC:点双连通分量。

若移除某个节点或边后,图被分割成多个连通块,则该节点或边称为割点或桥。

边双连通分量

边双连通分量是指不包含桥的最大连通子图。

核心思想

通过 Tarjan 算法,可以找到所有的桥和边双连通分量。判断桥的条件是:

low[v] > dfn[u],表示从 v 无法通过其他路径回到 u。

代码实现


#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int dfn[N], low[N], timestamp;
bool is_bridge[N];
vector<pair<int, int>> edges;
vector<int> adj[N];
stack<pair<int, int>> stk;

void tarjan(int u, int parent_edge) {
    dfn[u] = low[u] = ++timestamp;
    for (auto &edge_idx : adj[u]) {
        auto &[v, edge_id] = edges[edge_idx];
        if (!dfn[v]) {
            stk.push({u, edge_id});
            tarjan(v, edge_id);
            low[u] = min(low[u], low[v]);
            if (low[v] > dfn[u]) {
                is_bridge[edge_id] = true;
            }
        } else if (edge_id != parent_edge) {
            low[u] = min(low[u], dfn[v]);
            if (dfn[v] < dfn[u]) stk.push({u, edge_id});
        }
    }
}

点双连通分量

点双连通分量是指不包含割点的最大连通子图。

核心思想

通过 Tarjan 算法,可以找到所有的割点和点双连通分量。判断割点的条件是:

  • 根节点:至少有两个子树。
  • 非根节点:low[v] >= dfn[u]

代码实现


#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int dfn[N], low[N], timestamp;
bool is_cutpoint[N];
vector<int> adj[N];
stack<int> stk;
int dcc_count;

void tarjan(int u, bool is_root) {
    dfn[u] = low[u] = ++timestamp;
    stk.push(u);
    int child_count = 0;

    for (auto &v : adj[u]) {
        if (!dfn[v]) {
            tarjan(v, false);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                if (!is_root || ++child_count > 1) is_cutpoint[u] = true;
                vector<int> dcc;
                while (true) {
                    int w = stk.top(); stk.pop();
                    dcc.push_back(w);
                    if (w == v) break;
                }
                dcc.push_back(u);
                dcc_count++;
            }
        } else {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
标签: GraphTarjan

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。