关于无向图中连通分量缩点的问题

本文详细介绍了如何将边-双连通分量合并为一棵树,以及如何通过DFS遍历图来实现这一目标。同时,文章还阐述了点-双连通分量合并的实现方法,包括使用并查集来标记点是否属于同一连通分量。此外,文章提供了代码实例,展示了如何通过标记边和记录边来确定连通分量。

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

1、把边-双连通分量合并

一般情况:是要把边-双连通分量合并成一个点 , 然后用所有的桥组成一个新图 , 这样得到的新图就是一棵树了

方法:所有点只属于一个双连通分量 , 所以,对于求边-双连通分量,我们只要把所有桥都标记 , 然后再用dfs来遍历这图并且不经过桥,那么我们得到的就是一个边-双连通分量,根据这个原理 , 我们用并查集来标记两个点是不是属于同一个边-双连通分量,如果当前这条边时桥,那么我们就不标记他们,否则我们就把这两个点所属的树合并成一颗树。

代码:
#include
#include
#include
using namespace std;

#define maxn 110
#define min(x , y)  (x)<(y)?(x):(y)
int grap[maxn][maxn] , low[maxn];
int pre[maxn] , edge[maxn*maxn][2];
int n , m;
int dfs_clock , cnt;
int p[maxn];

void init()
{
    memset(grap , 0 , sizeof(grap));
    memset(pre , 0 , sizeof(pre));
    dfs_clock = cnt = 0;
    for(int i = 1; i <= n; i++)
        p[i] = i;
}

int find(int x)
{
    int g = x , h;
    while(p[x] != x)  x = p[x];

    while(p[g] != x)
    {
        h = p[g];
        p[g] = x;
        g = h;
    }

    return x;
}

void Union(int x , int y)
{
    int g = find(x) , h = find(y);
    if(g != h)  p[g] = h;
}

int dfs(int u)
{
    int lowu = low[u] = ++dfs_clock;
    int i , lowv;
    for(i = 1; i <= n; i++)
        if(grap[u][i])
        {
            grap[u][i] -= 1;
            grap[i][u] -= 1;
            if(!pre[i])
            {
                pre[i] = 1;
                lowv = dfs(i);
                lowu = min(lowv , lowu);
                if(lowv > low[u]) //记录桥
                    edge[cnt][0] = u , edge[cnt++][1] = i;
                else Union(u , i);
            }
            else if(lowu > low[i])
                lowu = low[i];
        }

    return lowu;    
}

int main()
{
    int i , j , x , y;
    for(i = 1; i <= n; i++)
        if(!pre[i])
        {
            pre[i] = 1;
            dfs(i);
        }
    return 0;
}

2、把点-双连通分量合并

方法:对于点-双连通分量来说 , 每个割点都是属于多个点-双连通分量,对于这种情况,我们就应该通过记录边来确定连通分量,因为每条边只属于一个点-连通分量,所以我们就用一个stack来存储遍历到的边 , 每当我们碰到割点时,就把这些边上的所有点存储。

代码:
//对于求割点 , 有两种标记的方法:1、标记边已经走过的边 ; 2、记录每个点的父亲节点,来判断当前不能被遍历的点是不是父亲节点
int dfs(int u , int fa)
{
    int lowu = low[u] = ++dfs_clock;
    int i , lowv;
    for(i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].u;
        gh.u = u;
        gh.next = v;
        if(!pre[v])
        {
            xy.push(gh);
            pre[v] = 1;
            lowv = dfs(v , u);
            lowu = min(lowv , lowu);
            if(lowv >= low[u])
            {
                //cout<<u<<endl;
                dfs_node++;
                new_edge[dfs_node].clear();
                for(;;)
                {
                    node cf = xy.top();  xy.pop();
                    if(p[cf.u] != dfs_node)  
                    {
                        p[cf.u] = dfs_node ;
                        new_edge[dfs_node].push_back(cf.u);
                    }
                    if(p[cf.next] != dfs_node)
                    {
                        p[cf.next] = dfs_node ;
                        new_edge[dfs_node].push_back(cf.next);
                    }
                    if(cf.u == u && cf.next == v)  break;
                }
            }
        }
        else if(v != fa && low[u] > low[v])
        {
            lowu = min(lowu , low[v]) ; //这里这条边,可记录 , 也可不记录
            xy.push(gh);
        }
    }

    return lowu;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值