题目描述
思路解析动画文字版
怎么判?换个说法:把「分两拨」翻译成「染两种颜色」:相邻的点必须不同色。只要能染成功,就是二分图。
为什么不用枚举:颜色像多米诺骨牌:第一个点一定色,后面全被推着走。所以根本不用试 2 的 n 次方种染法,顺着边「染过去」就行。
优解:染色法(BFS):走一遍图,边走边染、相邻就反色;一旦遇到「该反色却已经同色」的边,说明劈不开,直接 false。下面一帧染一个点、一帧查一条边地演给你看。
例一 · 全部未染:先把例一画好:四个点围成方框,四条边 0–1、1–2、2–3、3–0。一开始谁都没染色。
起点 0 染 A · 入队:随便挑起点 0,给它染上颜色 A(蓝)放进队列。它将带动整个连通块的染色。
取出 0 · 准备看邻居:从队列取出 0(紫色高亮 = 正在处理)。它是 A,接下来逐条看它的边,邻居都该染成反色 B。
查边 0–1 · 1 没染:看边 0–1(高亮)。1 还没染,染成反色 B(绿)入队。0 蓝、1 绿,这条边合法。
查边 0–3 · 3 没染:再看 0 的另一条边 3–0(高亮)。3 也没染,同样染成 B(绿)入队。0 的两条边都跨了色,合法。
取出 1 · 准备看邻居:取出 1(B/绿,紫色高亮)。0 退回普通蓝。1 是 B,它的邻居该染成 A。
查边 1–0 · 已染,比一下:看边 1–0(高亮)。0 已经染过了,就比一下颜色:0 是 A、1 是 B,不同色,合法,跳过。
查边 1–2 · 2 没染:看边 1–2(高亮)。2 还没染,染成 A(蓝)入队。此刻分组成形:A 拨 = {0,2},B 拨 = {1,3}。
1 处理完 · 放回队列看下一个:1 的两条边都查完、没撞色,它处理完毕(变绿)。四个点这下都染上了色,队列里还剩 3、2 等着核对。
取出 3 · 全是老边:取出 3(B/绿,高亮)。它连 0(A)和 2(A),两条边都已染好(高亮)且颜色都和 3 不同,全合法,无需新染。
取出 2 · 队列将空:取出 2(A/蓝,高亮)。它连 1、3,都是 B,和 2 不同色,合法。队列空了,整张图染完都没撞过色。
例一收工 · 是二分图:全染完且从没撞色:A 拨 = {0,2}(蓝),B 拨 = {1,3}(绿)。能干净劈成两拨 → true。
反例 · 藏着三角形:看反例 graph=[[1,2,3],[0,2],[0,1,3],[0,2]]。这里 0、1、2 互相都连,形成一个三角形——它会让染色撞墙。
起点 0 染 A · 入队:同样从 0 起步,染A(蓝)入队。它连着 1、2、3 三个点,等会儿出队就会把它们都逼成 B。
取出 0 · 查边 0–1:出队 0(高亮),看边 0–1。1 没染,染 B(绿)入队。0 蓝 1 绿,这条边目前合法。
查边 0–2 · 2 染 B:再看边 0–2。2 也没染,同样染 B(绿)入队。注意:1 和 2 之间也有一条边,而它俩现在都是绿!
查边 0–3 · 3 染 B:0 的最后一条边 0–3:3 没染,染 B(绿)入队。0 的三个邻居全是 B——隐患已经埋下。
取出 1 · 查边 1–2:出队 1(B/绿),查它的边 1–2(高亮)。可 2 也是 B(绿)!相邻两点同色,这条边劈不开,立刻返回 false。
本质:奇数环:记住这个结论:一张图是二分图,当且仅当它没有奇数长度的环。三角形就是最小的奇环,所以反例必然 false。
套路模板:凡是「分两组 / 相邻不同 / 站队对立」的题,都能套这个两色染色模板。
易错实演 · 不连通图:这张图分两块:{0,1} 和 {2,3,4}。从 0 跑只染好了上面一块,第二块 2、3、4 还全是灰的。
外层 for 接着跑第二块:外层 for 走到没染的 2,重启一次 BFS 把第二块也染好(2、4 蓝,3 绿)。这就是为什么必须遍历每个点。
边界三连:无边永远 true;最小的反例是三角形。记住边越密越容易出奇环。
面试追问:面试常追问 DFS/BFS/并查集三种写法,以及怎么顺手把两组成员输出。
参考代码
from collections import dequeclass Solution: def isBipartite(self, graph): n = len(graph) color = [0] * n # 0 未染, 1/-1 两色 for s in range(n): # 每个连通块各跑一次 if color[s] != 0: continue color[s] = 1 q = deque([s]) while q: u = q.popleft() for v in graph[u]: if color[v] == 0: # 没染 → 染反色 color[v] = -color[u] q.append(v) elif color[v] == color[u]: # 撞色 return False return True复杂度
- 时间:O(V + E),每个点入队出队一次,每条边最多检查两次
- 空间:O(V),color 数组 + 队列,最坏都装下所有点
易错点
面试追问把动画讲成自己的话
追问DFS 和 BFS 哪个更好?
追问怎么用并查集做?
追问如果要返回具体的两组分别有谁呢?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
找到最终的安全状态
LeetCode 802 · 中等 · 沿着 图论套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题