1. 二分图基础概念与核心价值
二分图是图论中一个非常重要的概念,它的定义很简单:一个图的顶点集可以被划分为两个互不相交的子集,使得每条边的两个端点分别属于这两个不同的子集。换句话说,图中不存在连接同一子集内顶点的边。
我第一次接触二分图是在解决一个课程安排问题时。假设我们有一组老师和一组课程,每位老师只能教授特定的几门课程,如何安排才能使最多的课程被教授?这个问题就可以完美地用二分图建模,其中老师是一个顶点集,课程是另一个顶点集,边表示老师能够教授的课程。
二分图在实际应用中非常广泛,比如:
- 社交网络中的用户和兴趣匹配
- 电商平台中的用户和商品推荐
- 云计算中的任务和服务器资源分配
- 生物信息学中的蛋白质相互作用分析
判断一个图是否是二分图的关键在于它是否可以被二着色(即用两种颜色给顶点着色,使得相邻顶点颜色不同)。这个性质引出了我们接下来要介绍的染色法。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 染色法判断二分图
2.1 染色法的基本原理
染色法的核心思想非常简单:尝试用两种颜色(通常称为0和1)给图中的顶点着色,要求相邻顶点颜色不同。如果能成功完成整个图的着色,那么这个图就是二分图;如果在着色过程中出现矛盾(即某个顶点无法满足相邻顶点颜色不同的要求),则不是二分图。
这里有个关键点:二分图一定不包含奇数长度的环。这个性质非常重要,因为:
- 如果一个图包含奇数环,那么在环上交替着色时,最后会回到起点发现颜色矛盾
- 没有奇数环的图一定是二分图(虽然这个证明需要一些图论知识,但记住这个结论对实际应用很有帮助)
2.2 染色法的实现细节
在实际编码实现时,我们通常使用DFS或BFS来遍历图并进行着色。下面是一个基于DFS的实现模板:
python复制def is_bipartite(graph):
n = len(graph)
color = [-1] * n # -1表示未着色,0和1表示两种颜色
def dfs(u, c):
color[u] = c
for v in graph[u]:
if color[v] == -1:
if not dfs(v, 1 - c):
return False
elif color[v] == c:
return False
return True
for i in range(n):
if color[i] == -1:
if not dfs(i, 0):
return False
return True
这个实现有几个关键点需要注意:
- 使用递归DFS进行深度优先遍历
- 着色时交替使用0和1两种颜色
- 遇到已着色的顶点时检查是否冲突
- 需要处理不连通图的情况(所以外层有for循环)
时间复杂度是O(V+E),其中V是顶点数,E是边数,因为每个顶点和边都只被访问一次。
2.3 染色法的实际应用案例
让我们看一个LeetCode上的实际问题:785. 判断二分图。题目给定一个无向图,要求判断它是否是二分图。
python复制class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
n = len(graph)
color = [-1] * n
for i in range(n):
if color[i] == -1:
queue = [i]
color[i] = 0
while queue:
