二分图的基本概念
什么是二分图?
二分图(Bipartite Graph)是指一个图的顶点集可以被分割为两个互不相交的子集 U U U 和 V V V, 并且图中的每一条边都连接 U U U 中的一个顶点和 V V V 中的一个顶点. 换句话说, 二分图中的顶点可以被分成两组, 组内的顶点之间没有边相连, 组间的顶点通过边相连.
二分图的性质
-
如果两个集合中的点分别染成红色和绿色, 可以发现二分图中的每一条边都一定是连接一个红色点和一个绿色点.
-
二分图不存在长度为奇数的环
上述两个性质均可以用来判断一个图是否为二分图.
二分图的判定方法
染色法
染色法是判定二分图最常用的方法. 其核心思想是: 使用两种颜色对图中的顶点进行染色, 相邻顶点颜色不同. 如果能够成功完成染色, 则该图是二分图; 否则, 不是二分图.
算法步骤:
- 选择一个起始顶点, 将其染成红色.
- 遍历该顶点的所有邻居, 将其染成绿色.
- 递归地对每个邻居的邻居染成红色, 依此类推.
- 如果在染色过程中发现某个顶点的颜色与相邻顶点颜色相同, 则该图不是二分图.
染色法的具体步骤可以用 BFS 或者 DFS 实现.
DFS 示例:
现有一个图如下, 请判断是否为二分图:
按照染色法, 从 A 点开始红绿交替染色, 可以得到如下结果:
DFS 代码实现
核心代码:
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 代码实现
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
的作用, 即用来判断顶点是否被访问过. 这是为了节省空间.
#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;
}
};