如果在图G中去掉一个顶点(自然同时去掉与该顶点相关联的所有边)后,该图的连通分支数增加,则称该顶点为G的割点
怎么去找图的割点呢?
我们容易想到的是遍历图,每次删除一个顶点,删除该顶点后,检查图的连通分支数是否增加,如果增加,该点就是割点,否则不是。
大牛们找到了有逼格的算法。。。
用了一个“时间戳”:记录这是第几次遍历的该顶点。和low数组:记录不经过父节点的能回到最小的时间戳。
看了那么久的算法,意思就是说:如果去掉一个顶点,这个顶点的子孩子回不到了它的祖先结点,那么去掉的这个顶点就是割点;所以每次的更新low[]的值,如果low[v]>=num[u];那顶点u就是割点;(类似于一句俗话:没有你爸爸哪有你(嘿嘿你在自己想想这个算法像不像这句话));
关于更新low[]的值在:一直遍历完节点后更新当前节点的low值,如果遍历到的顶点已经遍历过,更新当前节点的low与遍历过的该节点的时间戳比较
与算法书上的代码不同之处:我用的是邻接表
用的是两个数组头结点head[]和放边集的结构体e
关键点在:head[e[i].u]放的是节点e[i].u的第一条边的编号,e[i].next放的是编号为i的“下一条边”的编号、
for(i=边个数)
e[i].next=head[e[i].u];
head[e[i].u]=i;
/*
6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6
2
*/
#include<stdio.h>
#include<string.h>
#define min(a,b) a<b?a:b
using namespace std;
int n,m,root;
int num[9],low[9],flag[9],index,t;
int head[9];
struct edge
{
int u,v;
int w;
int next;
}e[9*9];
void dfs(int cur,int f)
{
int c=0,i,j;
index++;
num[cur]=low[cur]=index;
int k=head[cur];
while(k!=-1)
{
i=e[k].v;
if(num[i]==0)
{
c++;
dfs(i,cur);
low[cur]=min(low[cur],low[i]);
if(cur!=root && low[i] >= num[cur])
{
flag[cur]=1;
}
if(cur==root && c==2)
{
flag[cur]=1;
}
}
else if(i!=f)
{
low[cur]=min(low[cur],num[i]);
}
k=e[k].next;
}
}
int main()
{
int i,j,x,y,t=0;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
memset(num,0,sizeof(num));
memset(low,0,sizeof(low));
memset(flag,0,sizeof(flag));
for(i=1;i<=n;i++)
{
e[i].w=0;
}
for(i=1;i<=m+m;i+=2)
{
scanf("%d%d",&e[i].u,&e[i].v);
e[i].next=head[e[i].u];
head[e[i].u]=i;
e[i+1].u=e[i].v;
e[i+1].v=e[i].u;
e[i+1].next=head[e[i+1].u];
head[e[i+1].u]=i+1;
}
root=1;
dfs(1,root);
for(i=1;i<=n;i++)
{
if(flag[i]==1)
printf("%d ",i);
}
return 0;
}