图的割点(边)算法

参考啊哈算法第八章第三节,第四节:
先介绍图的割点算法
采用两种存储方法:
1.数组存储
2.图的邻接表法

第一种方法:

#include <stdio.h>

int min(int a,int b)
{
    return a<b?a:b;
}

int n,m,e[9][9];
int root,num[9],low[9],flag[9],index;//index用来进行时间戳的递增

//割点算法核心
void dfs(int cur,int father)
{
    int child=0,i;  //child用来记录生成树中当前顶点cur的儿子数量
    index++;
    num[cur]=index;//当前cur的时间戳
    low[cur]=index;//当前cur能访问到最早的时间戳。刚开始肯定是自己啦。
    for(i=1;i<=n;i++)//枚举与当前顶点cur有边相连的顶点i
    {
        if(e[cur][i]==1)
        {
            if(num[i]==0)//如果当前顶点的时间戳为零说明当前顶点还没有被访问过
            {
                child++;
                dfs(i,cur);//继续往下深度优先遍历搜索,此时i为cur的儿子
                //更新当前顶点cur能访问到最早定点的时间戳,因为如果他的儿子比他访问的还早,那么他肯定不需要通过父亲就能访问到
                low[cur]=min(low[cur],low[i]);
                //如果当前节点不是根节点并且满足low[i]>=num[cur],则当前顶点为割点
                if(cur!=root&&low[i]>=num[cur])
                    flag[cur]=1;
                if(cur==root&&child==2)
                    flag[cur]=1;
            }
            //如果当前顶点i曾经被访问过,并且这个顶点不是当前顶点cur的父亲。则说明i为cur的祖先,
            //因为i不可能为cur的儿子,如果为儿子,在第一个if语句中就判断了,因此需要更新当前节点cur能访问到最早节点的时间戳
            //自反的相邻边不满足。
            else if(i!=father)
                low[cur]=min(low[cur],num[i]);
        }
    }
    return;
}

int main()
{
    int x,y;
    scanf("%d%d",&n,&m);
    //初始化
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            e[i][j]=0;
    //读入每条边
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        e[x][y]=1;
        e[y][x]=1;
    }
    root=1;
    dfs(1,root);//从1号节点开始进行深度优先遍历
    for(int i=1;i<=n;i++)
        if(flag[i])
            printf("%d ",i);
    return 0;
}

对for循环里面的做一些改变

if(i==cur||i==father||!e[cur][i]) continue;
//有儿子并且判断儿子是否为祖宗。
if(num[i]==0)
{
    child++;
    dfs(i,cur);
    low[cur]=min(low[cur],low[i]);
    if(cur!=root&&low[i]>=num[cur])
        flag[cur]=1;
    if(cur==root&&child==2)
        flag[cur]=1;
}
else low[cur]=min(low[cur],num[i]);

采用图的邻接表存储法:

#include <stdio.h>

int u[15],v[15],w[15];
int first[7],next[15];

int min(int a,int b)
{
    return a<b?a:b;
}

int n,m,e[9][9];
int root,num[9],low[9],flag[9],index;//index用来进行时间戳的递增

//割点算法核心
void dfs(int cur,int father)
{
    int child=0;  //child用来记录生成树中当前顶点cur的儿子数量
    index++;
    num[cur]=index;//当前cur的时间戳
    low[cur]=index;//当前cur能访问到最早的时间戳。刚开始肯定是自己啦。
    //枚举与当前顶点cur有边相连的顶点i
    int k=first[cur];
    while(k!=-1)
    {
        if(w[k]==1)
        {
            if(num[v[k]]==0)//如果当前顶点的时间戳为零说明当前顶点还没有被访问过
            {
                child++;
                dfs(v[k],cur);//继续往下深度优先遍历搜索,此时i为cur的儿子
                //更新当前顶点cur能访问到最早定点的时间戳,因为如果他的儿子比他访问的还早,那么他肯定不需要通过父亲就能访问到
                low[cur]=min(low[cur],low[v[k]]);
                //如果当前节点不是根节点并且满足low[i]>=num[cur],则当前顶点为割点
                if(cur!=root&&low[v[k]]>=num[cur])
                    flag[cur]=1;
                if(cur==root&&child==2)
                    flag[cur]=1;
            }
            //如果当前顶点i曾经被访问过,并且这个顶点不是当前顶点cur的父亲。则说明i为cur的祖先,
            //因为i不可能为cur的儿子,如果为儿子,在第一个if语句中就判断了,因此需要更新当前节点cur能访问到最早节点的时间戳
            else if(v[k]!=father)
                low[cur]=min(low[cur],num[v[k]]);
        }
        k=next[k];
    }

    return;
}

int main()
{
    int x,y;
    scanf("%d%d",&n,&m);

    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        w[i]=1;
    }
    for(int i=m+1;i<=2*m;i++)
    {
        u[i]=v[i-m];
        v[i]=u[i-m];
        w[i]=1;
    }
    for(int i=1;i<=n;i++)
        first[i]=-1;
    for(int i=1;i<=2*m;i++)
    {
        next[i]=first[u[i]];
        first[u[i]]=i;
    }

    root=1;
    dfs(1,root);//从1号节点开始进行深度优先遍历
    for(int i=1;i<=n;i++)
        if(flag[i])
            printf("%d ",i);
    return 0;
}
/*
6 7
1 4
1 3
4 2
3 2
2 5
2 6
5 6
*/

图的割边算法:
只需要将上述程序改变一个条件

low[v]>=num[u]
改为
low[v]>num[u]

因为low[v]>=num[u]代表点v不可能在不经过父节点回到祖先的(包括父亲),所以顶点u是割点,如果low[v]>num[u]则表示连父亲都回不到了,倘若顶点v不能回到祖先,也没有另外一条边能回到父亲,那么u-v这条边就是割边,代码如下(只对上边做做一些修改):

if(num[i]==0)
{
    dfs(i,cur);
    low[cur]=min(low[cur],low[i]);
    if(low[i]>num[cur])
        printf("%d-%d\n",cur,i);
}
else if(i!=father)
    low[cur]=min(low[cur],num[i]);
if(num[v[k]]==0)
{
    dfs(v[k],cur);
    low[cur]=min(low[cur],low[v[k]]);
    if(low[v[k]]>num[cur])
        printf("%d-%d\n",cur,v[k]);
}
else if(v[k]!=father)
    low[cur]=min(low[cur],num[v[k]]);
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值