二分图判定

141 阅读4分钟

二分图判定

二分图是一个经典的图论算法

概念

如果一个图G=(V, E)是一个二分图,当且仅当顶点集V可以被分割为两个不相交的子集V1和V2,使得图中的每条边都连接一个属于V1的顶点和一个属于V2的顶点。

二分图是一种特殊的图,其中所有的顶点可以被划分为两个不相交的集合,使得图中任意两个相邻的顶点属于不同的集合。换句话说,可以将图中的顶点分成两组,使得同一组内的顶点之间没有边相连。二分图也被称为二部图或偶图

如下图结构, 分成了{a,c,e} 和{b,d,f}两组顶点, 同一组顶点之间没有边相连

a - b
  \
c - d
  /
e - f

严格的二分图中不能存在环, 如果存在环, 则在一个组合中一定会有边相连. 如果放宽定义的话, 可能存在自环(一个顶点与其自身相连).

应用

现实中很多实体之间的关系可以形成二分图的结构,在现实中有很多应用场景.

  1. 匹配问题:二分图可以用于解决各种匹配问题,其中目标是找到最大的匹配或最佳的匹配。例如,在稳定婚姻问题中,可以使用二分图来建模男女之间的喜好关系,并找到使得每个人都能够与对方配对的最佳结果。

  2. 调度问题:二分图可以用于调度问题,如任务分配或资源分配。通过将任务或资源表示为顶点,并将任务和资源之间的依赖关系表示为边,可以使用二分图算法进行有效的调度规划。

  3. 社交网络分析:社交网络的分析和挖掘中经常会用到二分图。例如,根据用户的兴趣和行为,可以构建一个用户-兴趣的二分图,用于推荐相关兴趣或寻找潜在的社交关系。

  4. 图像分割:在计算机视觉和图像处理领域,二分图被广泛用于图像分割任务。通过将图像的像素表示为一个集合,将像素之间的相似性关系表示为边,可以使用二分图算法将图像分割成不同的区域或对象。

  5. 传感器网络:在传感器网络中,二分图可以用于优化节点之间的通信和能量消耗。通过将传感器节点表示为一个集合,将节点之间的通信需求表示为边,可以使用二分图算法来找到最佳的通信方案。

算法

可以通过染色法判断二分图。步骤如下:

  1. 选择一个起始节点,并将其染色为颜色 A。
  2. 对该节点的所有邻接节点执行以下步骤: 2.1 如果邻接节点未被染色,将其染色为相反的颜色(如果当前节点染色为 A,则邻接节点染色为 B;如果当前节点染色为 B,则邻接节点染色为 A)。 2.2 如果邻接节点已经被染色,并且与当前节点的颜色相同,则该图不是二分图,算法结束。
  3. 继续对每个未染色的节点重复步骤 2,直到所有节点都被染色或确定该图不是二分图。
  4. 如果在遍历过程中没有发现颜色冲突,即所有节点都成功染色且满足相邻节点染有不同的颜色,那么该图就是一个二分图。

这种染色的方法可以使用深度优先搜索(DFS)或广度优先搜索(BFS)来实现。具体实现方式可以根据编程语言和数据结构的不同而有所差异,但上述步骤提供了一个通用的框架来判断图是否是二分图。


class Solution {
    constructor() {
        this.graph = [];
        this.isBipartGraph = true;
    }
        
    dfs(src) {
        if (!this.graph[src].length)
            return;
        
        for (const target of this.graph[src]) {
            if (this.visited.has(target) && this.color[target] === this.color[src]) {
                this.isBipartGraph = false;
                return;
            }
            if (!this.isBipartGraph)
                return;
            
            if (!this.visited.has(target)) {
                this.visited.add(target);
                this.color[target] = !this.color[src];
                this.dfs(target);
            }
        }
    }
    // 是否是二分图
    isBipartition(graph) {

        this.visited = new Set();
        this.color = new Array(n);
        this.isBipartGraph = true;
        
        for (let node = 0; node < graph.length; node++) {
            if (!this.visited.has(node)) {
                this.color[node] = true;
                this.visited.add(node);
            }
            this.dfs(node);
        }
        
        return this.isBipartGraph;
    }
}

/**
 * 测试用例, 图结构如下
 * 0 - 1
 * 2 - 3
 */
const test_graph = [[1], [0], [2], [3]]
solution = new Solution()
solution.isBipartition(test_graph) // => true