前置知识点:图的DFS 树,DFS 序
无向图的连通分量
连通分量是指一个无向图的一个极大连通子图,连通分量中的任意两个点都存在一条路径可以直接或间接互相到达。
对于一个无向图,如何求出它的所有连通分量呢?
算法一:图上 DFS
对于一个没有访问过的点,我们从这个点开始DFS,访问所有还没有访问过的点,每访问到一个点,我们就将其标记为已访问。在某一个点 DFS 结束时,所有在这一轮访问到的点就在一 个连通分量中。显然该算法的时间复杂度为 O(n+m)O(n+m) 。
参考代码:
const int N = 1e5+5;
int n, m;
int fa[N], vis[N], root; // fa[i]: 节点 i 所在连通分量的代表元素
int vector<int> g[N];
void dfs(int u) {
vis[u] = 1;
fa[u] = root;
for(auto v : g[u]) {
if(!vis[v])
dfs(v);
}
}
for(int i = 1; i <= n; i++) {
if(!vis[i]) {
root = i;
dfs(i);
}
}
算法二:并查集
通过扫描所有的边,不断合并两个连通分量,最终形成图的各个连通分量。算法时间复杂度为: O(n+mα(n))O(n+mα(n)) 。
参考代码:
const int N = 1e5+5;
int n, m;
int fa[N];
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
fa[x] = y;
}
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
merge(u, v);
}
}
❓问题: 在对图进行遍历后,能否快速判断某个连通分支是一棵树?
🚩回答: 如果使用 DFS 遍历来实现,那么只要在扩展时,走到走过的节点,就说明存在“冗余的边”,不可能是树。如果使用并查集,那么在合并时如果两个节点的祖先相同,则说明存在“冗余的边”,不可能是树。
练习题
无向图的割点和桥
前置概念(定义)
点连通度 :在一个无向连通图中,如果有一个顶点集合 VV ,删除顶点集合 VV 以及与 VV 中顶点相连(至少有一端在 VV 中)的所有边后,原图不连通,就称这个点集 VV 为 割点集合 。大小为 11 的点割集又被称作 割点 。
一个图的点连通度 :最小割点集合中的顶点数。
边连通度 :在一个无向连通图中,如果有一个边集合 EE ,删除这个边集 EE 中的边以后,原图不连通,就称这个边集 EE 为 割边集合 。
一个图的边连通度 :最小割边集合中的边数。大小为 11 的边割集又被称作 桥 。
点双连通图 :如果一个无向连通图的点连通度大于 11 ,则称该图是点双连通的(point biconnected),简称点双连通或点重连通(即删去任意一点原图仍然连通)。换句话说, 没有割点的连通图是点双连通的 。
边双连通图 :如果一个无向连通图的边连通度大于 11 ,则称该图是边双连通的(edge biconnected),简称边双连通或边重连通(即删去任意一条边原图仍然连通)。换句话说, 没有桥的连通图是边双连通的 。
割点 :一个图有割点,当且仅当这个图的点连通度为 11 时,割点集合的唯一元素被称为割点(cut point)(即删去割点原图不连通)。一个图可能有多个割点。有割点的图不一定有割边,如下图所示,结点 33 为割点,但是图中没有割边。

割边(桥) :如果一个无向连通图的边连通度大于 11 ,则称该图是边双连通的(edge biconnected),简称双连通或重连通(即删去任意一边原图仍然连通)。一个图有桥,当且仅当这个图的边连通度为 11 时,割边集合的唯一元素被称为桥(bridge)(即删去桥原图不连通)。一个图可能有多个桥。有割边的图也不一定有割点,如下图所示。

根据定义做两个猜想:两个割点之间的边一定是割边。割边的两个端点一定是割点。
可惜的是,这两个猜想都是错误的!反例如下图:

上图中,结点 3,43,4 为割点,但是边 (3,4)(3,4) 不是割边。边 (7,8),(8,9)(7,8),(8,9) 为割边,但点 7,97,9 不是割点。
通俗版定义
对于一个无向图,如果把一个点删除后这个图的连通块(极大连通分量)数增加了,那么这个点就是这个图的割点(又称割顶)。
Tarjan 求无向图的割点和桥
根据著名计算机科学家 Robert Tarjan 的名字命名的 Tarjan 算法能够在线性时间内求出无向图的割点与桥,进一步可求出无向图的双连通分量。在有向图方面,Tarjan 算法能够求出有向图的强连通分量、必经点与必经边(下一节将会介绍)。Robert Tarjan 在数据结构方面也做出了很多卓有成效的工作,包括证明并查集的时间复杂度,提出斐波那契堆、Splay Tree 和 Lint-Cut Tree 等。
1. 时间戳
在图的 深度优先遍历 过程中,按照每个节点第一次被访问的时间顺序,依次给予 NN 个节点 1∼N1∼N 的整数标记,该标记就被称为“时间戳”,记为 dfn[x]dfn[x] 。
2. 搜索树
在无向连通图中任选一个节点出发进行深度优先遍历,每个点只访问一次。所有发生递归的边 (x,y)(x,y) (换言之,从 xx 到 yy 是对 yy 的第一次访问)构成一棵树,我们把它称为“无向连通图的搜索树”。当然,一般无向图(不定连通)的各个连通块的搜索树构成无向图的“搜索森林”。
下图左侧展示了一张无向连通图,绿色节点是深度优先遍历的起点,加粗的边是“发生递归”的边(假设我们在遇到多个分支时,总是优先访问最靠左的一条)。右侧展示了深度优先遍历的搜索树,并标注了节点的时间戳。


1742

被折叠的 条评论
为什么被折叠?



