染色法判定二分图 — DFS深搜 +BFS宽搜

染色法判定二分图 — DFS深搜

题目描述

给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环。

请你判断这个图是否是二分图。

输入格式

第一行包含两个整数 n n n m m m

接下来 m m m 行,每行包含两个整数 u u u v v v,表示点 u u u 和点 v v v 之间存在一条边。

输出格式

如果给定图是二分图,则输出 Yes,否则输出 No。

数据范围

1 ≤ n , m ≤ 1 0 5 1 \leq n,m \leq 10^5 1n,m105

输入样例:

4 4
1 3
1 4
2 3
2 4

输出样例:

Yes

稀疏图用邻接表存储,因为n和m差不多大所以是稀疏图,如果m远大于n,就算稠密图,n为顶点数,m是边数

染色法判定二分图 — DFS深搜 +BFS宽搜_第1张图片

题解

解法1:DFS

首先我们需要了解什么是二分图。二分图,顾名思义,就是可以把图中的节点分成两个集合,使得同一集合内的节点没有边相连。因此,如果给定的图是二分图,也就是说,可以用两种颜色对节点进行染色,使得相邻节点的颜色不同。因此,我们可以使用深度优先搜索(DFS)来实现二分图的判断。

具体来说,我们可以从任意一个节点开始,将其染成红色,并遍历与其相邻的节点,将这些节点染成蓝色。接下来,对于每个蓝色的节点,再遍历与其相邻的节点,将这些节点染成红色。重复以上操作,直到所有的节点都被染色。如果染色过程中出现了相邻节点颜色相同的情况,则说明给定的图不是二分图。

具体实现时,我们可以使用一个颜色数组 c o l o r color color,用来记录每个节点的颜色。初始时,所有节点的颜色都是未染色的(可以用 − 1 -1 1 表示)。在 DFS 过程中,对于当前遍历到的节点 u u u,如果它没有被染色,则将其染成与其相邻节点的颜色相反的颜色,并继续遍历与其相邻的未染色节点;如果它已经被染色,并且颜色与与其相邻节点的颜色相同,则说明给定的图不是二分图。最终,如果所有的节点都被染色了,并且没有相邻节点颜色相同的情况出现,则说明给定的图是二分图。

时间复杂度:

DFS 的时间复杂度为 O ( n + m ) O(n+m) O(n+m),其中 n n n 表示节点数目, m m m 表示边数目。

#include
#include
using namespace std;
const int N = 2e5+10;
int h[N], e[N], ne[N], idx;
int n,m;
int color[N];

void add(int a,int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

bool dfs(int u,int c){
    color[u] = c;//将点u染色为c
    for(int i = h[u];i!=-1;i = ne[i]){//继续染色它的子结点
        int j = e[i];//子结点编号
        if(!color[j]){//如果子结点j未被染色,就将它染为与父结点不同的颜色
            if(!dfs(j,3 - c)){//染色过程出现冲突
                return false;
            }
        }else if(color[j] == c){//子结点颜色相同
            return false;
        }
    }
    return true;

}

int main(){
    cin>>n>>m;
    int u,v;
    memset(h,-1,sizeof h);
    while(m--){
        cin>>u>>v;
        add(u,v),add(v,u);
    }

    for(int i = 1;i<=n;i++)//可能存在非连通图
    if(!color[i])
        if(!dfs(i,1)){
            cout<<"No";
            return 0;
        }
    cout<<"Yes";
    return 0;
}


解法2:BFS

除了 DFS,我们还可以使用广度优先搜索(BFS)来实现二分图的判断。

具体来说,我们可以从任意一个节点开始,将其染成红色,并将其入队列。接下来,每次从队列中取出一个节点 u u u,遍历与其相邻的节点 v v v。如果 v v v 没有被染色,则将其染成与 u u u 相反的颜色,并将其入队列;如果 v v v 已经被染色,并且颜色与 u u u 相同,则说明给定的图不是二分图。

具体实现时,我们可以使用一个颜色数组 c o l o r color color,用来记录每个节点的颜色。初始时,所有节点的颜色都是未染色的(可以用 − 1 -1 1 表示)。在 BFS 过程中,对于当前遍历到的节点 u u u,如果它没有被染色,则将其染成与其相邻节点的颜色相反的颜色,并将其入队列;如果它已经被染色,并且颜色与与其相邻节点的颜色相同,则说明给定的图不是二分图。最终,如果所有的节点都被染色了,并且没有相邻节点颜色相同的情况出现,则说明给定的图是二分图。

时间复杂度:

BFS 的时间复杂度为 O ( n + m ) O(n+m) O(n+m),其中 n n n 表示节点数目, m m m 表示边数目。

#include 
#include 
#include 
#include 

using namespace std;

const int N = 100010, M = 2 * N;

int h[N], e[M], ne[M], idx;
int color[N]; // 用来记录每个节点的颜色,-1 表示未染色,0 表示染成红色,1 表示染成蓝色

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool bfs(int u)
{
    queue<int> q;
    q.push(u);
    color[u] = 0;

    while (q.size())
    {
        int t = q.front();
        q.pop();

        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (color[j] == -1)
            {
                color[j] = !color[t];
                q.push(j);
            }
            else if (color[j] == color[t])
                return false;
        }
    }

    return true;
}

int main()
{
    memset(h, -1, sizeof h);

    int n, m;
    cin >> n >> m;

    while (m -- )
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }

    bool flag = true;
    memset(color, -1, sizeof color);
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!bfs(i))
            {
                flag = false;
                break;
            }

    if (flag) puts("Yes");
    else puts("No");

    return 0;
}

你可能感兴趣的:(笔记,算法,数据结构,深度优先,宽度优先,算法)