定义
在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。
如果某个割点集合只含有一个顶点X(也即{X}是一个割点集合),那么X称为一个割点。
如果图是连通的,那么割点的定义就是,在一个无向连通图中,如果删除某个顶点后,图不再连通,则这个顶点为割点。
通过Tarjan算法可以求出所有割点,时间复杂度为O(N + M)。
例题
洛谷P3388
给定一个无向图(不一定连通),求出所有割点。
#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <map>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <cassert>
using namespace std;
const int MAXN = 20005;
const int MAXM = 200005;
struct Edge {
int dest, next;
} edges[MAXM];
int head[MAXN];
int edges_cnt = 0;
int n, m;
int low[MAXN]; // 记录每个结点在不结果父结点时,能够回到的最小时间戳
int dfn[MAXN]; // 记录每个结点的时间戳
bool cut[MAXN]; // 记录每个结点是否为割点
int dfi = 0; // 当前时间戳
int root; // 根结点
void addEdge(int src, int dest) {
++edges_cnt;
edges[edges_cnt].dest = dest;
edges[edges_cnt].next = head[src];
head[src] = edges_cnt;
}
void tarjan(int node, int father) {
int child = 0; // 记录生成树中当前结点node的孩子数量
dfn[node] = low[node] = ++dfi;
for (int i = head[node]; i; i = edges[i].next) {
int dest = edges[i].dest;
if (!dfn[dest]) { // 如果dest的时间戳为0,说明它还没被访问过
++child;
tarjan(dest, node);
// 更新当前结点node能访问到的最早时间戳
low[node] = min(low[node], low[dest]);
// 如果当前结点不是根结点且low[dest]>=dfn[node],则当前结点为割点
if (node != root && low[dest] >= dfn[node]) {
cut[node] = true;
}
// 如果当前结点是根结点,在生成树中必须有2个孩子,才是割点
else if (node == root && child == 2) {
cut[node] = true;
}
}
// 否则如果dest曾被访问过,并且不是node的父结点,则说明此时dest为
// node的祖先,因此需要更新node能访问到的最早时间戳
else if (dest != father) {
low[node] = min(low[node], dfn[dest]);
}
}
}
int main() {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
#endif
scanf("%d%d", &n, &m);
while (m--) {
int src, dest;
scanf("%d%d", &src, &dest);
addEdge(src, dest);
addEdge(dest, src);
}
for (int node = 1; node <= n; ++node) {
if (!dfn[node]) {
root = node;
tarjan(node, 0);
}
}
int cut_cnt = 0;
for (int node = 1; node <= n; ++node) {
if (cut[node])
++cut_cnt;
}
printf("%d\n", cut_cnt);
for (int node = 1; node <= n; ++node) {
if (cut[node])
printf("%d ", node);
}
putchar('\n');
return 0;
}