C++算法篇:DFS超详细解析(2)--- tarjan算法求无向图割边

本文介绍了C++中利用DFS和Tarjan算法来寻找无向图的割边。内容涵盖DFS的基本框架,详细解释了Tarjan算法的原理,以及如何判断割边。同时,文章还提及了割边的定义和无向图桥的概念,并简要提及了2-连通图。
摘要由CSDN通过智能技术生成

<<<上一篇

系列文章目录

①:无向图基本概念
②:tarjan算法求无向图割边


前言
第一次写算法,讲得肯不透彻,有误还请指教awa



一、回顾

先来回顾一下dfs的基本框架:

//存图方式:vector(g[N])
void dfs(int u){
   //u:当前节点
	vis[u]=true;
	for(int& v:g[u]){
   //访问u连到的每个节点
		if(!vis[v]) dfs(v);
	}
}

二、tarjan算法

请注意,这里讲的仅限于无向图,有向图的算法会稍有不同。

  1. 首先我们可以确定的是回边一定不是桥1
  2. 那么就只需要判断树边了。
    回顾割边的定义,删边后两端断开,那么就意味着一端的点永远无法通过一条路径回到另一端,在DFS-tree上就体现为下方的节点永远无法通过非树边回到上方的点。如果成立,即只能通过树边相连,那么这条树边就是桥了!(因为是树,每个节点以自己为终点只会有一个直系父亲)


    tarjan算法定义了两个数组:depth[]low[],定义如下:
  • depth[i]:节点i在DFS-tree上的深度。
  • low[i]:在dfs-tree上,以节点i及其子孙为起点的回边回到的 最低 高度。

假设我们已经算出每个节点的depth(下简称dep)和low,如何判断割边呢?
(务必注意,deplow越大意味着越晚被遍历)
分类讨论一下吧:(假设目前在处理tree2上u->v边)

  1. l o w [ v ] < = d e p [ u ] low[v]<=dep[u]

下面是使用 C++ 实现使用并查集找到无向图中的桥的代码,不使用 Tarjan 算法: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; int n, m, tot; int head[MAXN], dfn[MAXN], low[MAXN], fa[MAXN]; bool vis[MAXN]; struct Edge { int to, nxt; } e[MAXN << 1]; void addEdge(int u, int v) { e[++tot].to = v; e[tot].nxt = head[u]; head[u] = tot; } int find(int x) { if (x == fa[x]) { return x; } return fa[x] = find(fa[x]); } void merge(int x, int y) { int fx = find(x), fy = find(y); if (fx != fy) { fa[fx] = fy; } } void dfs(int u, int p) { dfn[u] = low[u] = ++tot; vis[u] = true; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v == p) { continue; } if (!dfn[v]) { dfs(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { printf("%d %d is bridge\n", u, v); } else { merge(u, v); } } else if (vis[v]) { low[u] = min(low[u], dfn[v]); } } vis[u] = false; } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) { fa[i] = i; } for (int i = 1; i <= m; i++) { int u, v; scanf("%d %d", &u, &v); addEdge(u, v); addEdge(v, u); } dfs(1, 0); return 0; } ``` 在这个代码中,我们使用了 DFS 遍历来查找桥。具体来说,我们记录了每个节点的 dfn 和 low 值,同时使用 vis 数组来判断一个节点是否已经被访问过。如果一个节点的 low 值大于其子节点的 dfn 值,则说明该节点与子节点之间的边是桥。否则,我们将该节点与子节点合并到同一个连通块中。 需要注意的是,这个算法的时间复杂度为 $O(m \alpha(n))$,其中 $m$ 为边数,$n$ 为节点数,$\alpha(n)$ 是反阿克曼函数的某个值,可以认为是一个非常小的常数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值