图论基础算法: 二分图的判定(C++)

二分图的基本概念

什么是二分图?

二分图(Bipartite Graph)是指一个图的顶点集可以被分割为两个互不相交的子集 U U U 和 V V V, 并且图中的每一条边都连接 U U U 中的一个顶点和 V V V 中的一个顶点. 换句话说, 二分图中的顶点可以被分成两组, 组内的顶点之间没有边相连, 组间的顶点通过边相连.

二分图的性质

  • 如果两个集合中的点分别染成红色和绿色, 可以发现二分图中的每一条边都一定是连接一个红色点和一个绿色点.

  • 二分图不存在长度为奇数的环

上述两个性质均可以用来判断一个图是否为二分图.


二分图的判定方法

染色法

染色法是判定二分图最常用的方法. 其核心思想是: 使用两种颜色对图中的顶点进行染色, 相邻顶点颜色不同. 如果能够成功完成染色, 则该图是二分图; 否则, 不是二分图.

算法步骤:
  1. 选择一个起始顶点, 将其染成红色.
  2. 遍历该顶点的所有邻居, 将其染成绿色.
  3. 递归地对每个邻居的邻居染成红色, 依此类推.
  4. 如果在染色过程中发现某个顶点的颜色与相邻顶点颜色相同, 则该图不是二分图.

染色法的具体步骤可以用 BFS 或者 DFS 实现.

DFS 示例:

现有一个图如下, 请判断是否为二分图:

按照染色法, 从 A 点开始红绿交替染色, 可以得到如下结果:

DFS 代码实现

核心代码:

cpp 复制代码
bool ColorDFS(const Graph& graph, std::vector<Color>& colors, Vertex start,
              Color color) {
  colors[start] = color;
  for (auto v : graph.Adj(start)) {
    if (colors[v] == color) return false;
    if (colors[v] == Color::Unknown) {
      if (!ColorDFS(graph, colors, v,
                    color == Color::Red ? Color::Black : Color::Red)) {
        return false;
      }
    }
  }
  return true;
}

完整的代码请参考: Bipartite.ixx


BFS 代码实现
cpp 复制代码
bool ColorBFS(const Graph& graph, std::vector<Color>& colors, Vertex start) {
  std::queue<Vertex> q;
  q.push(start);
  colors[start] = Color::Red;

  while (!q.empty()) {
    Vertex v = q.front();
    q.pop();
    Color opposite_color =
        (colors[v] == Color::Red) ? Color::Black : Color::Red;

    for (auto w : graph.Adj(v)) {
      if (colors[w] == colors[v]) {
        return false; // Found an edge connecting two vertices of the same
        // color
      }
      if (colors[w] == Color::Unknown) {
        colors[w] = opposite_color;
        q.push(w);
      }
    }
  }
  return true;
}

完整的代码请参考: Bipartite.ixx


LeetCode 例题解析

785. 判断二分图

题目描述: 给定一个无向图, 判断它是否是一个二分图.

解题思路: 使用染色法对图进行染色, 如果在染色过程中发现冲突, 则该图不是二分图.

代码实现:

下面代码中用-1表示未染色的顶点, 0表示顶点染成红色, 1表示顶点染成红色, 这样颜色转换就非常容易(1-color表示另外一个颜色). 同时colors数据也兼具visited的作用, 即用来判断顶点是否被访问过. 这是为了节省空间.

cpp 复制代码
#include <vector>
#include <queue>

using namespace std;

class Solution {
  bool ColorDFS(vector<vector<int>>& graph, std::vector<int>& colors, int start,
                int color) {
    colors[start] = color;
    for (auto v : graph[start]) {
      if (colors[v] == color) return false;
      if (colors[v] == -1) {
        if (!ColorDFS(graph, colors, v, 1 - color)) {
          return false;
        }
      }
    }
    return true;
  }

  bool ColorBFS(const vector<vector<int>>&  graph, std::vector<int>& colors,
                    int start, int color) {
    std::queue<int> q;
    q.push(start);
    colors[start] = color;

    while (!q.empty()) {
      int v = q.front();
      q.pop();
      auto opposite_color = 1 - colors[v];

      for (auto w : graph[v]) {
        // Found an edge connecting two vertices of the same color
        if (colors[w] == colors[v]) {
          return false;
        }
        if (colors[w] == -1) {
          colors[w] = opposite_color;
          q.push(w);
        }
      }
    }
    return true;
  }

public:
  bool isBipartite(vector<vector<int>>& graph) {
    vector<int> colors(graph.size(), -1);
    for (int i = 0; i < graph.size(); ++i) {
      if (colors[i] == -1) {
        // 此处用 DFS 替代也可以通过
        if (!ColorBFS(graph, colors, i, 0)) {
          return false;
        }
      }
    }
    return true;
  }
};
相关推荐
循着风4 小时前
二叉树的多种遍历方式
数据结构·算法
老猿讲编程8 小时前
C++中的奇异递归模板模式CRTP
开发语言·c++
.格子衫.9 小时前
022数据结构之树状数组——算法备赛
数据结构·算法·1024程序员节
黑科技Python9 小时前
生活中的“小智慧”——认识算法
学习·算法·生活
Yupureki9 小时前
从零开始的C++学习生活 16:C++11新特性全解析
c语言·数据结构·c++·学习·visual studio
紫荆鱼10 小时前
设计模式-迭代器模式(Iterator)
c++·后端·设计模式·迭代器模式
sali-tec10 小时前
C# 基于halcon的视觉工作流-章52-生成标定板
开发语言·图像处理·人工智能·算法·计算机视觉
IT古董10 小时前
【第五章:计算机视觉-项目实战之推荐/广告系统】2.粗排算法-(4)粗排算法模型多目标算法(Multi Task Learning)及目标融合
人工智能·算法·1024程序员节
熬了夜的程序员10 小时前
【LeetCode】89. 格雷编码
算法·leetcode·链表·职场和发展·矩阵
应茶茶10 小时前
C++11 核心新特性:从语法重构到工程化实践
java·开发语言·c++