一、什么是点双连通分量?
在一个图的一个任意分量中,任意两个点的相连都存在至少两条点不重复的路径。是不是很难理解?其实也可以这样理解:把这个分量从该图中分离出来,如果该分量中没有割点,那么该分量就是一个点连通分量。比如:
如果把1,2,3结点及他们的边分离出来,就成了一个三角,在这个分量中是不存在割点的。
二、算法思维
现在我们知道点双连通分量个割点有关,所以代码的整体结构也和割点的差不多。我们在求割点时,已经完成了对一次极大点双连通分量的访问。所以我们只需要把我们访问过的边(这里一定是存储边,用栈)存储起来,在后面判断割点时,若是一个点是割点,则输出该割点到下一个割点中间的边,这些边组合起来就是一个点双连通分量。
为什么是存储边呢? 因为一个割点属于至少一个点双连通分量,存点会出现某个点双连通分量缺少点的情况。
三、代码实现
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
typedef struct edge { //存入栈中的边
int from, to;
}Edge;
vector<int>G[20]; //存所有边
stack<Edge>s;
int low[20], num[20]; //割点数组
int dfn = 0; //递归深度
void dfs(int u, int fa) { //整体代码和割点差不多,加上了入栈和输出
Edge* tail;
low[u] = num[u] = ++dfn;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
tail = new edge;
if (!num[v]) {
tail->from = u; tail->to = v; //入栈边
s.push(*tail);
dfs(v, u);
low[u] = min(low[u], low[v]);
if (num[u] <= low[v]) { //碰到割点直接输出 直到这个割点连接的边出栈
cout << "分界线" << endl; //输出
while (true) {
int v = s.top().from;
cout << s.top().from << "---" << s.top().to << endl;
s.pop();
if (v == u) break; //出栈停止条件
}
cout << endl;
}
}
else if (num[u] > num[v] && v != fa) { //若指向已经访问过的点 也应该把改变存储起来 比如成环
low[u] = min(low[u], num[v]); //v != fa 防止了1--2 2--1的出现
tail->from = u; tail->to = v;
s.push(*tail);
}
}
}
int main() {
int N, M, from, to;
cout << "请输入点数 和 边数:";
cin >> N >> M;
memset(low, 0, sizeof(low));
memset(num, 0, sizeof(num));
dfn = 0;
for (int i = 1; i <= M; i++) {
cin >> from >> to;
G[from].push_back(to);
G[to].push_back(from);
}
for (int i = 1; i <= N; i++) //防止它不是连通图
if (!num[i])
dfs(i, -1);
cout << endl;
system("pause");
return 0;
}
--请多指教