二分图简介

定义:二分图的顶点集可分割为两个互不相交的子集,图中每条边依附的两个顶点都分属于这两个子集,且两个子集内的顶点不相邻。image

image
首先,二分图作为一种特殊的图模型,会被很多高级图算法(比如最大流算法)用到,不过这些高级算法我们不是特别有必要去掌握,有兴趣的读者可以自行搜索。
从简单实用的角度来看,二分图结构在某些场景可以更高效地存储数据。
image
每个电影节点的相邻节点就是参演该电影的所有演员,每个演员的相邻节点就是该演员参演过的所有电影,非常方便直观。类比这个例子,其实生活中不少实体的关系都能自然地形成二分图结构,所以在某些场景下图结构也可以作为存储键值对的数据结构(符号表)。

判定二分图

首先我们介绍双色问题:给定一幅图,请你用两种颜色将图中的所有顶点着色,且使得任意一条边的两个端点的颜色都不相同。
判定二分图的算法很简单,就是用代码解决「双色问题」。说白了就是遍历一遍图,一边遍历一遍染色,看看能不能用两种颜色给所有节点染色,且相邻节点的颜色都不相同。可以采用DFS或者BFS。

例题1

看一道例题:判断二分图
image
image
代码如下:

class Solution {
public:
    vector<int> color;
    vector<int> vis;
    bool flag = true;
    bool isBipartite(vector<vector<int>>& graph) {
        int n = graph.size();
        color.resize(n), vis.resize(n);
        fill(color.begin(), color.begin() + n, 0);
        fill(vis.begin(), vis.begin() + n, 0);
        // 因为图不一定是联通的,可能存在多个子图
        // 所以要把每个节点都作为起点进行一次遍历
        // 如果发现任何一个子图不是二分图,整幅图都不算二分图
        for(int v = 0; v < n; v++)
        {
            if((!vis[v]))
            {
                vis[v] = 1;
                DFS(graph, v);
            }
        }
        return flag;
    }

    void DFS(vector<vector<int>>& graph, int v)
    {
        // 如果已经确定不是二分图了,就不用浪费时间再递归遍历了
        if(!flag) return;

        for(auto& p: graph[v])
        {
            // 相邻节点 p 没有被访问过
            // 那么应该给节点 p 涂上和节点 v 不同的颜色
            if(!vis[p])
            {
                color[p] = (color[v] == 0 ? 1 : 0);
                vis[p] = 1;
                DFS(graph, p);
            }
            else
            {
                // 相邻节点 p 已经被访问过
                // 根据 v 和 p 的颜色判断是否是二分图
                if(color[v] == color[p])
                {
                    flag = false;
                    return;
                }
            }
        }
    }
};

例题2

可能的二分法
image
显然,把人看作图中的节点,把讨厌的关系看作是边,两个互相讨厌的人不能在相邻节点,则意味着图中相邻节点不能在同一组。所以该题目等价为双色问题。
代码如下:

class Solution {
public:
    bool color[2001] = {false};
    bool vis[2001] = {false};
    bool flag = true;
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) 
    {
        //编号从1开始
        //建图
        vector<vector<int>> graph(n + 1, vector<int>());
        for(auto& p : dislikes)
        {
            int v = p[0];
            int w = p[1];
            //无向图相当于双向图
            graph[v].push_back(w);
            graph[w].push_back(v);
        }
        for(int v = 1; v <= n; v++)
        {
            if(!flag) return false;
            
            if(!vis[v])
            {
                vis[v] = true;
                DFS(graph, v);
            }
        }
        return flag;
    }
    void DFS(vector<vector<int>> graph, int v)
    {
        if(!flag) return;
        for(auto& p : graph[v])
        {
            if(!vis[p])
            {
                color[p] = !color[v];
                vis[p] = true;
                DFS(graph, p);
            }
            else
            {
                if(color[p] == color[v])
                {
                    flag = false;
                    return;
                }
            }
        }
    }
};

参考文献

二分图的判定

posted on 2023-04-15 17:23  sc01  阅读(24)  评论(0编辑  收藏  举报