前言
在图论里,二分图是一类结构非常特殊、应用极广的无向图。本文用通俗语言讲清什么是二分图、性质、判定方法,最后给出一份简洁可直接使用的 C++ 二分图判定代码。
一、什么是二分图
1. 定义
给定一个无向图,如果可以把图中所有顶点划分成两个互不相交的集合 {U,V},满足:
- 所有边的两个端点,一定一个在 U、一个在 V;
- 同一个集合内部的点之间没有边相连。
满足这个条件的图,就叫做二分图(二部图)。
形象理解: 把点分成左右两堆,所有边只能跨左右连,同一边内部不能有边。
2. 等价重要性质(核心)
一个无向图是二分图,当且仅当图中不存在长度为奇数的环(奇环)。
- 有奇环 → 一定不是二分图
- 没有奇环 → 一定是二分图
例子:
- 三角形(3 个点两两相连,环长 3,奇数)→ 不是二分图
- 一条链、四边形、树(树没有任何环)→ 都是二分图
3. 特殊情况
- 孤立点(没有任何边的点):可以随便分到任意一侧,不影响二分图性质;
- 树:所有树都是二分图(无环,自然无奇环)。
二、二分图的判定思路:染色法
思想
尝试用两种颜色给图中每个点染色,要求:
- 相邻的两个点颜色必须不同;
- 若染色过程中发现:一个点和已经染色的邻居颜色相同 → 存在奇环,不是二分图。
实现方式常用:DFS / BFS BFS 更安全,不会出现递归深度过大栈溢出的问题。
三、C++ 完整实现
功能:输入无向图,判断是否为二分图,输出结果。
使用说明
- 第一行输入点数 n、边数 m;
- 接下来 m 行每行输入两个点 \(u,v\),代表无向边;
- 程序自动遍历所有连通块,判断是否为二分图并输出。
cpp
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
vector<int> g[MAXN];
// -1未染色,0和1代表两种颜色
int color[MAXN];
int n, m;
bool ok = true;
void bfs(int s) {
queue<int> q;
q.push(s);
color[s] = 0;
while (!q.empty() && ok) {
int u = q.front();
q.pop();
for (int v : g[u]) {
if (color[v] == -1) {
color[v] = color[u] ^ 1;
q.push(v);
} else if (color[v] == color[u]) {
ok = false;
return;
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
memset(color, -1, sizeof color);
for (int i = 1; i <= m; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n && ok; ++i) {
if (color[i] == -1) {
bfs(i);
}
}
if (ok) cout << "Yes, it is a bipartite graph\n";
else cout << "No, not a bipartite graph\n";
return 0;
}
四、简单复杂度说明
每个点、每条边只会被访问一次,时间复杂度 \(O(n+m)\),可以处理较大范围的图。
五、拓展:二分图能干嘛?
判定只是基础,二分图常见经典问题:
- 二分图最大匹配(匈牙利算法)
- 最大独立集、最小顶点覆盖(二分图有对应定理)
- 部分网络流问题可转化为二分图模型求解