在问 · 算法题解

785. 判断二分图

中等图论广度优先搜索染色力扣原题 ↗

题目描述

有一张 n 个节点的无向图,节点编号为 0 到 n - 1。输入二维数组 graph,graph[u] 列出与节点 u 相连的所有节点。

图中没有自环和重复边;若 v 在 graph[u] 中,则 u 也在 graph[v] 中。图不一定连通。

如果能把全部节点分到两个集合里,使每条边的两个端点分别位于不同集合,这张图就是二分图。是二分图返回 true,否则返回 false。

示例 1
输入:graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
输出:false

解释:节点 0、1、2 两两相连构成三角形,无法分成两组。

示例 2
输入:graph = [[1,3],[0,2],[1,3],[0,2]]
输出:true

解释:可以分成 {0,2} 和 {1,3} 两组。

数据范围

  • graph.length == n
  • 1 <= n <= 100
  • 0 <= graph[u].length < n
  • 0 <= graph[u][i] <= n - 1
  • graph[u] 不含 u 本身。
  • graph[u] 中的值各不相同。
  • 若 v 在 graph[u] 中,则 u 也在 graph[v] 中。

解题思路:广度优先搜索染色

二分图等价于能用两种颜色给节点染色、且相邻节点颜色不同。对每个还没染色的节点作为起点做 BFS:起点染 0,出队节点 u 的每个邻居 v,如果未染色就染成与 u 相反的颜色并入队;如果已染色且与 u 同色,说明出现了奇数环,返回 false。所有连通块都染完没有冲突,返回 true。

为什么正确

已染色的节点中,任何一条已检查过的边两端颜色都不同。

复杂度分析

时间 O(V+E),每个节点入队一次、每条边检查两次;空间 O(V)。

易错点

图可能不连通,必须对每个未染色节点都启动一次 BFS,只从节点 0 出发会漏掉其他连通块里的奇数环。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        n = len(graph)
        color = [-1] * n
        for start in range(n):
            if color[start] != -1:
                continue
            color[start] = 0
            queue = collections.deque([start])
            while queue:
                u = queue.popleft()
                for v in graph[u]:
                    if color[v] == -1:
                        color[v] = color[u] ^ 1
                        queue.append(v)
                    elif color[v] == color[u]:
                        return False
        return True

载入编辑器,自己跑一遍 →

其他解法

染色法(广度或深度优先)是判断二分图的标准做法;并查集把“邻居同侧”变成集合合并,适合在线加边。

解法 2:并查集 时间 O((V + E) · α(V)),空间 O(V)

二分图中每个点的所有邻居都必须在另一侧,即邻居之间彼此同侧。对每个点 u,把它的所有邻居合并到第一个邻居所在的集合;合并前先检查 u 和邻居是否已经在同一集合,如果是,说明有一条边连着同侧的两点,不是二分图。

不需要显式染色和队列,边可以任意顺序处理,适合边动态加入的场景。但想法不如染色直观,也不能直接给出两侧的划分。

class Solution:
    def isBipartite(self, graph: List[List[int]]) -> bool:
        parent = list(range(len(graph)))

        def find(x):
            while parent[x] != x:
                parent[x] = parent[parent[x]]
                x = parent[x]
            return x

        for u, nbrs in enumerate(graph):
            for v in nbrs:
                if find(u) == find(v):
                    return False
                parent[find(v)] = find(nbrs[0])
        return True

图论 专题的其他题目

查看 图论 全部题目 →