LeetCode 785. 判断二分图
本节目标
对每个连通分量进行 BFS 二染色,发现相邻同色即判定不是二分图。
这是连通性、并查集与二分图中“相邻必须分到不同组”的母题。二分图不是要求图连通,而是要求每个连通分量都能被二染色。
题意与约束
邻接表 graph[u] 给出 u 的无向邻居。若所有边都能跨越两组,图是二分图;任意奇数长度环都会迫使起点最后与自己同色。孤立点没有边,可单独归入任意一组。重要的是:一个不含奇环的首分量不能证明整个图二分,后面的独立分量仍须检查。
直接思路与瓶颈
若枚举每个顶点属于两组的所有可能,n 个顶点有 2^n 种分法;仅靠局部猜测也可能在远处遇到同色相邻才发现矛盾。沿边传播“邻居必须相反”的颜色,可以一次遍历就记录并检验全部约束。
图模型与算法推导
用 -1 表示未染色,0、1 表示两组。对每个未染色起点,把它染为 0 并 BFS。队列中及已访问的每条边都满足“已确定的相邻点颜色不同”:遇到未染色邻居就赋为 1 - color[u],遇到已染色邻居则检查它不能与 u 同色。
如果一条边两端同色,沿 BFS 树从该分量起点到两个端点的路径会与这条边拼出奇环,约束矛盾;没有矛盾时,颜色数组本身就是一个合法划分。
正确性依据
对一个 BFS 分量,起点任取 0 不影响可行性。每次沿边为未染色邻居设置相反色,保持已处理边跨组;若已有邻居同色,任何二分划分都会违反该边,返回假正确。外层循环启动所有未染色分量,所以无一遗漏。全部遍历后颜色给出每条边的两端不同色,图为二分图。
样例执行过程
以三角形 graph = [[1,2],[0,2],[0,1]] 为例,初始 color = [0,-1,-1]、pending = [0]。弹出 0 后,邻居 1、2 都必须染成与 0 相反的颜色 1,此时 color = [0,1,1]、队列含 1、2。随后弹出 1:边 (1,0) 两端颜色不同,没有冲突;检查边 (1,2) 时发现两端都为 1,说明这个奇环无法二染色,立即返回 false。
代码实现
- C++
- Python
#include <queue>
#include <vector>
using namespace std;
class Solution {
public:
bool isBipartite(vector<vector<int>>& graph) {
vector<int> color(graph.size(), -1);
for (int start = 0; start < static_cast<int>(graph.size()); start++) {
if (color[start] != -1) continue;
queue<int> pending;
color[start] = 0;
pending.push(start);
while (!pending.empty()) {
int node = pending.front();
pending.pop();
for (int neighbor : graph[node]) {
if (color[neighbor] == -1) {
color[neighbor] = 1 - color[node];
pending.push(neighbor);
} else if (color[neighbor] == color[node]) {
return false;
}
}
}
}
return true;
}
};
from collections import deque
class Solution:
def isBipartite(self, graph: list[list[int]]) -> bool:
color = [-1] * len(graph)
for start in range(len(graph)):
if color[start] != -1:
continue
color[start] = 0
pending = deque([start])
while pending:
node = pending.popleft()
for neighbor in graph[node]:
if color[neighbor] == -1:
color[neighbor] = 1 - color[node]
pending.append(neighbor)
elif color[neighbor] == color[node]:
return False
return True
行为测试先放入含两条独立边和孤立点的合法图,再把奇环放在第一个分量之外,确保实现会从每个未染色顶点启动 BFS。
复杂度分析
每个顶点最多入队一次,每条无向边从邻接表两端各检查一次,时间 O(n + m);颜色和队列使用 O(n) 额外空间。
边界与易错点
- 外层循环必须遍历所有顶点,而非只从
0开始。 - 染色应在入队时完成,避免同一节点被多个邻居重复入队。
- 相邻已染色节点的颜色相同才失败;不同色应继续。
- 空图和只含孤立点的图都是二分图。
模式迁移
“敌人必须不同阵营”“相邻任务不能同班”都可转成二染色。若还需按距离分层,BFS 队列天然携带层序;若约束变为多种颜色或不等式,需考虑更一般的图着色或约束满足模型。