0
点赞
收藏
分享

微信扫一扫

图论相关之二分图

嚯霍嚯 2022-03-23 阅读 30

图论相关(三)

文章目录

1、介绍

定义:二分图的顶点集可分割为两个互不相交的子集,图中每条边依附的两个顶点都分属于这两个子集,且两个子集内的顶点不相邻
给定一幅「图」,请用两种颜色将图中的所有顶点着色,且使得任意一条边的两个端点的颜色都不相同,即图的上色问题

2、二分图的判定思路

说白了就是遍历一遍图,一边遍历一边染色,看看能不能用两种颜色给所有节点染色,且相邻节点的颜色都不相同

/* 二叉树遍历框架 */
void traverse(TreeNode root) {
    if (root == null) return;
    traverse(root.left);
    traverse(root.right);
}

/* 多叉树遍历框架 */
void traverse(Node root) {
    if (root == null) return;
    for (Node child : root.children)
        traverse(child);
}

/* 图遍历框架 */
boolean[] visited;
void traverse(Graph graph, int v) {
    // 防止走回头路进入死循环
    if (visited[v]) return;
    // 前序遍历位置,标记节点 v 已访问
    visited[v] = true;
    for (TreeNode neighbor : graph.neighbors(v))
        traverse(graph, neighbor);
}

图遍历框架可以稍微改为:

/* 图遍历框架 */
void traverse(Graph graph, boolean[] visited, int v) {
    visited[v] = true;
    // 遍历节点 v 的所有相邻节点 neighbor
    for (int neighbor : graph.neighbors(v)) {
        if (!visited[neighbor]) {
            // 相邻节点 neighbor 没有被访问过
            // 那么应该给节点 neighbor 涂上和节点 v 不同的颜色
            traverse(graph, visited, neighbor);
        } else {
            // 相邻节点 neighbor 已经被访问过
            // 那么应该比较节点 neighbor 和节点 v 的颜色
            // 若相同,则此图不是二分图
        }
    }
}

3、例题

785. 判断二分图 - 力扣(LeetCode) (leetcode-cn.com)

DFS:

// 记录图是否符合二分图性质
private boolean ok = true;
// 记录图中节点的颜色,false 和 true 代表两种不同颜色
private boolean[] color;
// 记录图中节点是否被访问过
private boolean[] visited;

// 主函数,输入邻接表,判断是否是二分图
public boolean isBipartite(int[][] graph) {
    int n = graph.length;
    color =  new boolean[n];
    visited =  new boolean[n];
    // 因为图不一定是联通的,可能存在多个子图
    // 所以要把每个节点都作为起点进行一次遍历
    // 如果发现任何一个子图不是二分图,整幅图都不算二分图
    for (int v = 0; v < n; v++) {
        if (!visited[v]) {
            traverse(graph, v);
        }
    }
    return ok;
}

// DFS 遍历框架
private void traverse(int[][] graph, int v) {
    // 如果已经确定不是二分图了,就不用浪费时间再递归遍历了
    if (!ok) return;

    visited[v] = true;
    for (int w : graph[v]) {
        if (!visited[w]) {
            // 相邻节点 w 没有被访问过
            // 那么应该给节点 w 涂上和节点 v 不同的颜色
            color[w] = !color[v];
            // 继续遍历 w
            traverse(graph, w);
        } else {
            // 相邻节点 w 已经被访问过
            // 根据 v 和 w 的颜色判断是否是二分图
            if (color[w] == color[v]) {
                // 若相同,则此图不是二分图
                ok = false;
            }
        }
    }
}

BFS:

/ 记录图是否符合二分图性质
private boolean ok = true;
// 记录图中节点的颜色,false 和 true 代表两种不同颜色
private boolean[] color;
// 记录图中节点是否被访问过
private boolean[] visited;

public boolean isBipartite(int[][] graph) {
    int n = graph.length;
    color =  new boolean[n];
    visited =  new boolean[n];
    
    for (int v = 0; v < n; v++) {
        if (!visited[v]) {
            // 改为使用 BFS 函数
            bfs(graph, v);
        }
    }
    
    return ok;
}

// 从 start 节点开始进行 BFS 遍历
private void bfs(int[][] graph, int start) {
    Queue<Integer> q = new LinkedList<>();
    visited[start] = true;
    q.offer(start);
    
    while (!q.isEmpty() && ok) {
        int v = q.poll();
        // 从节点 v 向所有相邻节点扩散
        for (int w : graph[v]) {
            if (!visited[w]) {
                // 相邻节点 w 没有被访问过
                // 那么应该给节点 w 涂上和节点 v 不同的颜色
                color[w] = !color[v];
                // 标记 w 节点,并放入队列
                visited[w] = true;
                q.offer(w);
            } else {
                // 相邻节点 w 已经被访问过
                // 根据 v 和 w 的颜色判断是否是二分图
                if (color[w] == color[v]) {
                    // 若相同,则此图不是二分图
                    ok = false;
                }
            }
        }
    }
}

886. 可能的二分法 - 力扣(LeetCode) (leetcode-cn.com)

这边要注意的是:

1、上面一题已经给定了邻接表(只不过是已数组给定),这边没有,要自己构建链接表

2、由于是无向图,所以两边都要添加

class Solution {
  boolean[] color;
    boolean[] visited;
    boolean ok = true;
    public boolean possibleBipartition(int n, int[][] dislikes) {
        color = new boolean[n+1];
        visited = new boolean[n+1];
        LinkedList<Integer>[] graph = new LinkedList[n+1];
        for (int i = 1; i <= n; i++) {
            graph[i] = new LinkedList<>();
        }
        for (int[] e : dislikes) {
            int from = e[0];
            int to = e[1];
            // 无向图,都要添加,上一题中给定的邻接表里是已经都添加了
            graph[from].add(to);
            graph[to].add(from);
        }
        // 上面一题已经给定了邻接表(只不过是已数组给定),这边没有,要自己构建链接表
        for (int i = 1; i <= n; i++) {
            dfs(i, graph);
        }
        return ok;
    }
    void dfs(int s, LinkedList<Integer>[] graph){

        if (!ok)
            return;
        visited[s] = true;
        for (int v : graph[s]){
            if (!visited[v]){
                color[v] = !color[s];
                dfs(v, graph);
            }else {
                if (color[v] == color[s]){
                    ok = false;
                    return;
                }
            }
        }

    }
}
BFS同上题一样
举报

相关推荐

0 条评论