一、什么是割点和割边?
1、割点:当在一个连通的图中,去除某个点,使得该图不再连通,而被去除的点就是该图的割点。
2、割边(桥):如割点一样,在一个连通的图中,去除某条边,使得该图不再连通,而被去除的条边就是该图的割边,也称桥。
二、如何判断是否为割点和割边?
在看文字的时候务必边画边看。
首先,我们需要引入一个时间轴(num)和连接轴(low)(实在时想不到啥名字了)。我们可以在脑海里想象一个图,1--2 2--3 3--4 4--2(无向图),我们从一端开始,假设它的编号为1,我们每次到达一个结点,都初始化num 和 low 当前时间 比如:我们从1结点开始 ,时间也从1开始,那么结点1的num 和 low都为1;结点2的num 和 low就为2。。。当结点来到4时,结点4指向的是2,此时,结点4的low 则改变为2;说明产生了回退边(该结点的孩子指向了当前结点之前)由于我们是通过DFS实现在回溯的同时更新前方已访问过的结点的low。这些步骤结束之后,开始直观判断割点,如果该结点的num < 孩子的low,说明改结点是割点 。 否则,说明孩子与比自己当前位置还前的结点有连接。所以,就算去除自己,孩子还是会通过另外一条路径与整个图相连。但值得注意的是开始的端点结点,它的时间最早,所以不管它的孩子的low值怎么变化 它的num 都小于孩子的low, 显然并不是所有的端点都是割点 ,对于这种情况我们用孩子数量的办法,当端点的孩子数量 多于1时说明是割点。
割边判断方法一样,当前结点的u的num < 孩子结点v的low时,且不需要判断开始点不需要另外判断,则割边为:u--v;
以下 是判断割点的代码:
#include <iostream>
#include <vector>
using namespace std;
vector<int>G[105]; //存边
int num[105], low[105]; //时间轴、连接轴。
int judge[105]; //判断是否为割点
int n, m, dfn; //点和边 时间(dfn)
void dfs(int u,int father) { //当前结点和双亲结点
low[u] = num[u] = ++dfn; //初始化时间轴和连接轴为当前时间。
int child = 0; //该结点的孩子 主要用于判断头结点是否为割点。
for (int j = 0; j < G[u].size(); j++) {
int v = G[u][j];
if (!num[v]) { //如果该结点未被访问
child++;
dfs(v, u);
low[u] = min(low[u], low[v]); //放在dfs()的后面,所以在此结点后的结点都已更新连接轴的值
if (num[u] <= low[v] && u != 1) { //如果当前结点(不是头结点)时间轴的值小于等于孩子的连接轴的值,则当前结点为割点
judge[u] = true;
}
}
else if (num[u] > num[v] && v != father) { //如果该结点已被访问过且当前结点u 的时间轴小于孩子的时间轴的值且孩子不是u的双亲,如果是双亲则没有任何意义这属于多重边
low[u] = min(low[u], num[v]); //更新u 连接轴的值
}
}
if (u == 1 && child > 1) //如果u == 1 时不能用上述程序实现,通过孩子数量来确定是否为割点。
judge[1] = true;
}
int main() {
int a, b;
while (cin >> n >> m) {
memset(low, 0, sizeof(low));
memset(num, 0, sizeof(num));
for (int i = 0; i <= n; i++)
G[i].clear();
dfn = 0;
for (int i = 1; i <= m; i++) {
cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
dfs(1, -1); //从1开始 双亲结点为-1.(属于初始化)
cout << "割点有:";
for (int i = 0; i <= n; i++) {
if (judge[i]) {
cout << i << " ";
}
}
cout << endl;
}
system("pause");
return 0;
}
一下为割边代码:
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
#define Max 20
typedef struct Edge {
int u, v;
}edge;
bool judge[Max]; //true 是割点 false 不是
vector<int>G[Max]; //存边
stack<Edge>s;
int num[Max], low[Max], dfn; //dfn来标记递归的层数;
edge* tail;
void dfs(int u, int fa) { //此次dfs的结点 和 父结点
low[u] = num[u] = ++dfn; //开始的low 和 nun 是相等的
int child = 0, v; //孩子个数 主要用于根结点判断割点
for (int i = 0; i < G[u].size(); i++) {
v = G[u][i];
if (!num[v]) { //未访问过
child++;
dfs(v, u);
low[u] = min(low[u], low[v]); //从孩子的low 中更新 low
if (num[u] < low[v]) //判断割点与这里不同;
{
tail = new Edge; tail->u = u; tail->v = v; s.push(*tail);
} //注意:若这里的判断条件改为 num < low 就成为了判断割边的条件 (当然割边不能在用judge[]来存储)
}
else if (num[v] < num[u] && v != fa) //对于已经访问过的点 必然存在 num ,这时需要更新low , = 此孩子的num.
low[u] = min(low[u], num[v]);
}
}
int main() {
int N, M, from, to;
cin >> N >> M;
memset(judge, false, sizeof(judge));
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);
}
dfs(1, -1);
edge *start;
cout << "--------" << endl;
while (s.size()) {
start = &(s.top());
s.pop();
cout << start->u << "---" << start->v << endl;
}
cout << endl;
system("pause");
return 0;
}
---请多指教