图的割点

本文介绍了一种高效寻找图中割点的算法,通过使用时间戳和low数组记录节点可达性的特性,来判断哪些顶点是割点。具体实现采用邻接表存储图结构,并通过深度优先搜索进行遍历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果在图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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值