今天学习了图的割点与桥的算法
图的割点以及桥
图的割点:割点是指在无向连通图中,某点和该点连接的边去掉以后图便不再连通
在上面的图片中(上面是一个有向图,我们当作无向图即可)我们知道当我们去掉A点之后,这个图就完完全全变成了俩个部分。
图的桥:桥是指去掉某一条边之后,这个图也是不连通的一个图
上面我们可以知道A-E、E-F是桥。
这俩个,都是建立在tarjan算法之上的,建议大家先去了解tanjan算法。
割点的特点:
因为这里是无向图,所以我们不能让当前结点的儿子节点去访问自己,可以访问到自己的祖宗节点。
割点的特征是:
- 是根节点,且至少有俩个儿子节点
- 不是跟节点,但是,本节点的dfn值<=儿子节点的low值。
为什么是这个特征?
因为根节点如果没有俩个儿子节点,它去除之后,并不能把图分开,但是如果有俩个以上,就能够分开这个图。
而不是根节点的部分,如果儿子节点的low值是大于dfn的,说明儿子节点是需要通过当前节点来访问到它自己(儿子节点),而当前节点的dfn值大于等于儿子节点的low值的话,说明儿子节点 要么可以访问到更早的节点(这是大于的情况),因为在无向图中,儿子节点可以访问的更早,就说明,那个更早的节点也是可以不通过当前节点去访问儿子节点的; 要么就是儿子节点能够通过其他节点访问到当前节点(因为我们规定儿子节点是不能访问当前节点的,这是等于的情况),通常这个情况是构成了一个环路了。
桥的特征是:
儿子节点的low值大于当前节点的dfn值。
为什么是这个特征?
因为当儿子节点的low值是大于当前节点的dfn值,说明儿子节点无法通过当前节点的父节点去寻找到另外一条路径,所以当儿子节点的low值大于当前节点的dfn值时,说明当前节点到儿子节点的这条边时桥。
C代码如下:
#include<stdio.h>
#define N 100
int n,m,top;
int e[N][N];
int timez,dfn[N],low[N],book[N];
int father[N];
int minz(int a,int b)
{
if(a<b) return a;
return b;
}
int dfs(int pre,int x)
{
dfn[x]=low[x]=++timez;
father[x]=pre;
int i,j,k;
for(i=1;i<=n;i++)
{
if(dfn[i]==0&&e[x][i])
{
dfs(x,i);
low[x]=minz(low[x],low[i]);
if(j>=2) book[x]=1;
if(dfn[x]<low[i]) printf("%d %d是桥\n",x,i);
}
else if(e[x][i]&&i!=pre)
{
low[x]=minz(low[x],dfn[i]);
}
}
return 0;
}
int main()
{
int i,j,u,v;
puts("请输入你所需要的顶点总数:");
scanf("%d",&n);
puts("请输入你所需要的边的总数");
scanf("%d",&m);
puts("请输入边的起点、终点:");
for(i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
e[u][v]=1;
e[v][u]=1;
}
puts("请输入顶点:");
scanf("%d",&top);
dfs(0,top);
for(i=1;i<=n;i++)
{
if(book[i]==1) printf("%d是割点\n",i);
}
return 0;
}
C++代码如下:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
const int N=100;
int n,m,top;
int e[N][N];
int timez,dfn[N],low[N],book[N];
int father[N];
int minz(int a,int b)
{
if(a<b) return a;
return b;
}
int dfs(int pre,int x)
{
dfn[x]=low[x]=++timez;
father[x]=pre;
int i,j,k;
for(i=1;i<=n;i++)
{
if(dfn[i]==0&&e[x][i])
{
dfs(x,i);
low[x]=minz(low[x],low[i]);
if(j>=2) book[x]=1;
if(dfn[x]<low[i]) cout << x << i << "是桥\n" ;
}
else if(e[x][i]&&i!=pre)
{
low[x]=minz(low[x],dfn[i]);
}
}
return 0;
}
int main()
{
int i,j,u,v;
cout << "请输入你所需要的顶点总数:" << endl;
cin >> n ;
cout << "请输入你所需要的边的总数" << endl;
cin >> m ;
cout << "请输入边的起点、终点:" << endl;
for(i=0;i<m;i++)
{
cin >> u >> v;
e[u][v]=1;
e[v][u]=1;
}
cout << "请输入顶点:" << endl;
cin >> top ;
dfs(0,top);
for(i=1;i<=n;i++)
{
if(book[i]==1) cout << i << "是割点\n" ;
}
return 0;
}