二分图判定(DFS/BFS/并查集)

一、二分图定义

二分图,是图论中的一种特殊模型,设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且同一集合中不同的两点没有边相连。看一个例子:
在这里插入图片描述
图中点集合分为A(包含2,4号顶点)和B(包含1,3,5,6号顶点),并且A,B集合中的点没有形成连接关系,显然这是一个二分图。

二、二分图判定

定理1:无向图G为二分图的一个充要条件是:
1、G中至少包含两个顶点
2、G中所有的回路长度都必须是偶数
也就是说,一个图是二分图的充分必要条件是这个图不包含长度为奇数的圈。

那么,怎么实现呢?
很简单,采用染色的方法,对于一个无向图,起初图中所有顶点都是无色,我们从任意未染色顶点出发,对这个顶点进行染色,对于与这个顶点相邻的的顶点,有三种情况:
1、未染色 那么将这个顶点染色,染与当前顶点不相同的颜色。
2、已经染色但是当前顶点颜色不同 那么跳过该顶点
3、已经染色但是与当前顶点相同。则该图不是一个二分图,返回失败。
图中所有顶点已经进行染色,并且没有出现相邻顶点颜色相同的情况,则该图是一个二分图。

三、代码实现

1、DFS实现思想也就是上面二分图判定所描述的。
在这里插入图片描述

/*描述:给定一个无向图,判断是否是二分图*/
/*思想:通过dfs染色的方法,从任意未遍历过的顶点出发进行染色,如果有与当前顶点染色相同的,则不是二分图
否则继续搜索与当前顶点相连的未染色的节点,并且染上相反的颜色,最后遍历完所有的顶点,相邻顶点没有同色,则为二分图。
时间复杂度O(n^2),空间复杂度O(n^2),若采用邻接表作为存储结构,则时间复杂度为O(n+m)
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int edge[maxn][maxn];//邻接矩阵存储
int color[maxn];//标记顶点颜色
int n,m;
bool dfs(int u,int c)
{
    color[u]=c;//对u点进行染色
    for(int i=1;i<=n;i++)//遍历与u点相连的点
    {
        if(edge[u][i]==1)//如果i点与u点相连
        {
            if(color[i]==c) return false;//i点的染色重复,则不是二分图
            if(!color[i]&&!dfs(i,-c)) return false;//该点未染色,染上相反的颜色.dfs继续搜索
        }
    }
    return true;//所有点染色完成之后,并且相邻顶点没有同色,则为二分图
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(edge,0,sizeof(edge));
        memset(color,0,sizeof(color));
        for(int i=0;i<m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            edge[u][v]=1;//无向图,因此uv和vu都需要
            edge[v][u]=1;
        }
        bool flag=false; //默认为二分图
        for(int i=1;i<=n;i++)
        {
            if(!color[i])
            {
                if(!dfs(i,1))
                {
                    printf("No\n");
                    flag=true;
                }
            }
        }
        if(!flag) printf("Yes\n");
    }
    return 0;
}
/*
测试数据:
4 3
1 4
2 3
3 4  Yes
6 8
1 2
1 3
2 4
3 4
3 5
3 6
4 6
5 6  No
*/

2、BFS思想主要是遍历这个图中每一层的时候,检查每一层是否有圈,有圈则不是二分图。
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int color[maxn];
vector<int>edge[maxn];//存储采用vector,相当于邻接表
int n,m;
bool bfs(int x,int c)
{
    queue<int>q;
    q.push(x);//从x号节点开始
    color[x]=c;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<edge[now].size();i++)
        {
            int v=edge[now][i];
            if(color[v]==0)//若v点未进行染色
            {
                color[v]=-color[now];//将v点染色,并且不与当前节点now节点相同
                q.push(v);//将v点放进队列继续访问
            }
            if(color[v]==color[now]) return false;//与当前节点颜色相同,该图不是二分图
        }
    }
    return true;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(color,0,sizeof(color));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        bool flag=true;
        for(int i=1;i<=n;i++)
        {
           if(color[i]==0&&!bfs(i,1))
           {
               flag=false;
               break;
           }
       }
        if(flag) printf("The Graph is two partite graph!\n");
        else     printf("The Graph is not two partite graph!\n");
    }
}

3、文件读写方式代码,采用fstream的两个子类ifstream和ofstream。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int color[maxn];
vector<int>edge[maxn];
int n,m;
int bfs(int x,int c)
{
   queue<int>q;
   q.push(x);
   color[x]=c;
   while(!q.empty())
   {
       int now=q.front();
       q.pop();
       for(int i=0;i<edge[now].size();i++)
       {
           int j=edge[now][i];
           if(color[j]==0)
           {
               color[j]=-color[now];
               q.push(j);
           }
           if(color[j]==color[now]) return false;
       }
   }
   return true;
}
int main()
{
   ifstream in("C:\\Users\\Administrator\\Desktop\\input.txt");
   ofstream out("C:\\Users\\Administrator\\Desktop\\output.txt");
   if(!in.is_open()) cout<<"open file input.txt failure"<<endl;
   if(!out.is_open()) cout<<"open file output.txt failure"<<endl;
   while(!in.eof())
   {
       memset(color,0,sizeof(color));
       in>>n>>m;
       if(in.fail()) break;//解决ifstream和ofstream文件尾读取两次问题
       for(int i=0;i<m;i++)
       {
           int u,v;
           in>>u>>v;
           edge[u].push_back(v);
           edge[v].push_back(u);
       }
       bool flag=true;
       for(int i=1;i<=n;i++)
       {
           if(color[i]==0&&!bfs(i,1))
           {
               flag=false;
               break;
           }
       }
       if(flag) out<<"YES"<<endl;
       else out<<"NO"<<endl;
   }
   in.close();
   out.close();
   return 0;
}

4、并查集实现二分图判定
思想:对于一条边的两个点将其与他们都不认识的人,也就是没有连接的点合并在同一个集合中,如果一条边两个点查找到的根节点一样,也就是说这两个节点已经在同一个集合中,说明这个图不是二分图。
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int father[maxn];
int findset(int x)//并查集路径压缩
{
    if(x!=father[x]) father[x]=findset(father[x]);
    return father[x];
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=2*n;i++) father[i]=i;
        bool flag=true;
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            int fa=findset(u);//查找根节点
            int fb=findset(v);
            int x=findset(u+n);//查找u不认识的人的根节点
            int y=findset(v+n);
            if(fa==fb) flag=false;
            else//将两个不认识的人合并在同一个集合中
            {
                father[x]=fb;
                father[y]=fa;
            }
        }
        if(flag) printf("YES\n");
        else     printf("NO\n");
    }
    return 0;
}

关于ifstream和ofstream参考:C++ifstream和ofstream的详细用法
关于二分图的匹配问题以及实现下面这篇文章中有详细介绍:二分图算法
关于并查集学习链接:并查集学习

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

童话ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值