深入理解二分图判定:Java BFS 实现与应用

152 阅读3分钟

在图论中,二分图(Bipartite Graph) 是指可以将所有节点分成 两个独立集合,并且同一个集合中的节点之间没有直接相连的无向图。判断图是否是二分图是许多应用的基础,例如:

  • 任务调度:将任务分配给不同的执行组,避免冲突
  • 资源分配:将用户与资源进行匹配,确保最优分配
  • 冲突检测:在社交网络、推荐系统中检测是否存在冲突组
  • 网络流优化:二分图匹配广泛用于最大流算法的优化

在本文中,我们将**基于 BFS(广度优先搜索)**实现 Java 版的二分图判定算法,并分析它的应用场景及优化方案。


1. 题目描述

给定一个无向图,判断它是否是二分图。
输入是一个邻接表 graph[i],表示与节点 i 相连的所有节点。

示例


Input: graph = {{1,3}, {0,2}, {1,3}, {0,2}}
Output: true
Explanation: 可以分成两个集合 {0,2} 和 {1,3},且没有内部连接。

2. Java 实现(BFS + 染色法)

代码实现


import java.util.*;

public class BipartiteGraphChecker {
    private static final int COLOR_ONE = 1;
    private static final int COLOR_TWO = -1;

    public boolean isBipartite(int[][] graph) {
        Map<Integer, Integer> visited = new HashMap<>();
        for (int i = 0; i < graph.length; i++) {
            if (!visited.containsKey(i)) {
                if (!bfs(i, visited, graph)) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean bfs(int node, Map<Integer, Integer> visited, int[][] graph) {
        Deque<Integer> queue = new ArrayDeque<>();
        queue.offer(node);
        visited.put(node, COLOR_ONE);

        while (!queue.isEmpty()) {
            Integer cur = queue.poll();
            int curColor = visited.get(cur);

            for (int neighbor : graph[cur]) {
                if (visited.containsKey(neighbor)) {
                    if (visited.get(neighbor) != -curColor) {
                        return false;
                    }
                } else {
                    queue.offer(neighbor);
                    visited.put(neighbor, -curColor);
                }
            }
        }
        return true;
    }

    public static void main(String[] args) {
        BipartiteGraphChecker checker = new BipartiteGraphChecker();
        int[][] graph = {{1, 3}, {0, 2}, {1, 3}, {0, 2}};
        System.out.println(checker.isBipartite(graph)); // 输出 true
    }
}

3. 代码解析

(1)思路:BFS + 染色法

  • 我们使用 visited 哈希表来存储每个节点的颜色:

    • COLOR_ONE = 1
    • COLOR_TWO = -1
  • 采用 BFS 遍历图,初始时给定一个起始节点,并为其染色 COLOR_ONE

  • 遍历该节点的所有邻居:

    • 若邻居未染色,则染成相反颜色并加入队列
    • 若邻居已经染色,但颜色相同,则说明不是二分图,返回 false

(2)代码优化

  • 使用 Deque<Integer> 替代 Queue<Integer> :提高队列操作性能
  • 使用 HashMap<Integer, Integer> 代替数组:适用于非连续节点编号的情况

4. 时间复杂度 & 空间复杂度

  • **时间复杂度:**O(V + E),其中 V 是节点数,E 是边数,每个节点最多遍历一次,每条边最多遍历两次
  • **空间复杂度:**O(V),存储 visited 哈希表和 queue

5. 二分图的实际应用

二分图广泛应用于计算机科学、网络结构和优化问题,下面是一些典型的应用场景:

应用领域应用场景二分图的作用
任务调度任务-执行者匹配任务与工作组形成二分图,确保任务合理分配
资源分配用户-服务器匹配通过二分图匹配实现最优资源分配
冲突检测社交网络、推荐系统发现对立用户组,防止冲突
最大流算法网络流优化通过最大匹配提高计算效率
数据库优化SQL 查询优化通过二分图分割,优化索引计算

例如:

  • Kubernetes Pod 调度 中,可以将 Pod可用节点 建立二分图,优化调度决策
  • DevOps 资源分配 中,服务器与任务调度可以建模为二分图匹配问题,提高资源利用率

6. 拓展:如何处理非连通图?

在现实应用中,图可能包含多个连通分量
解决方案:

  • for 循环中,遍历所有未访问的节点,确保所有连通分量都被遍历
  • 这也是为什么代码中的 for 循环遍历 graph.length 而不是仅检查 graph[0]

7. 总结

🔹 二分图的核心思想:可以用 BFS 或 DFS 进行图染色,如果出现冲突则不是二分图
🔹 BFS 实现更适合大规模图,相比 DFS 更不容易栈溢出
🔹 应用广泛,在 任务调度、DevOps 资源分配、冲突检测 等场景下有重要作用