CS-Wiki CS-Wiki
Home
知识体系总览
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • MySQL
  • Redis
  • 设计模式
  • Java 基础
  • Java 集合
  • Java 并发
  • Java 虚拟机
  • Spring
  • Kafka
  • 校招扫盲
  • 项目推荐
  • 唠唠嗑儿
  • 读书笔记
归档
GitHub (opens new window)
Home
知识体系总览
  • 数据结构与算法
  • 计算机网络
  • 操作系统
  • MySQL
  • Redis
  • 设计模式
  • Java 基础
  • Java 集合
  • Java 并发
  • Java 虚拟机
  • Spring
  • Kafka
  • 校招扫盲
  • 项目推荐
  • 唠唠嗑儿
  • 读书笔记
归档
GitHub (opens new window)
  • 刷题模板汇总
  • 一些刷题小技巧
  • 整数 and 位运算

  • 数组

  • 链表

  • 哈希表

  • 字符串

  • 栈

  • 队列

  • 二叉树

  • 前缀树

  • 二分查找

  • 双指针法

  • 区间求和问题

  • 排序

  • 回溯算法

  • 贪心算法

  • 动态规划

  • 图

    • 图理论基础
    • 图的搜索:DFS

      • 岛屿的最大面积:BFS 和 DFS 模板
      • 岛屿数量
      • 节点间通路
      • 二分图
        • 📃 题目描述
        • 🔔 解题思路
          • DFS
          • 递归
          • 迭代
          • BFS
        • 💥 复杂度分析
      • 所有路径:需要存储路径的 DFS 模板
      • 单词搜索:DFS + 回溯模板
      • 计算除法
      • 最长递增路径:记忆化 DFS 模板
      • 二叉树中所有距离为 K 的结点
      • 克隆图
      • 剑指 Offer 17 - 打印从 1 到最大的 n 位数
      • 剑指 Offer 35 - 复杂链表的复制
      • 展平多级双向链表
    • 图的搜索:BFS

    • 拓扑排序

    • 并查集

  • 数学

  • 自动机

  • 海量数据和空间限制

  • 05-数据结构与算法
  • 图
  • 图的搜索:DFS
小牛肉
2022-03-20
目录

二分图

# 📃 题目描述

题目链接:

  • 剑指 Offer II 106. 二分图 - 力扣(LeetCode) (leetcode-cn.com) (opens new window)
  • 785. 判断二分图 - 力扣(LeetCode) (leetcode-cn.com) (opens new window)

存在一个 无向图 ,图中有 n 个节点。其中每个节点都有一个介于 0 到 n - 1 之间的唯一编号。

给定一个二维数组 graph ,表示图,其中 graph[u] 是一个节点数组,由节点 u 的邻接节点组成。形式上,对于 graph[u] 中的每个 v ,都存在一条位于节点 u 和节点 v 之间的无向边。该无向图同时具有以下属性:

  • 不存在自环(graph[u] 不包含 u)。
  • 不存在平行边(graph[u] 不包含重复值)。
  • 如果 v 在 graph[u] 内,那么 u 也应该在 graph[v] 内(该图是无向图)
  • 这个图可能不是连通图,也就是说两个节点 u 和 v 之间可能不存在一条连通彼此的路径。

二分图定义:如果能将一个图的节点集合分割成两个独立的子集 A 和 B ,并使图中的每一条边的两个节点一个来自 A 集合,一个来自 B 集合,就将这个图称为 二分图。

如果图是二分图,返回 true ;否则,返回 false。

示例 1:

img

输入:graph = [[1,2,3],[0,2],[0,1,3],[0,2]]
输出:false
解释:不能将节点分割成两个独立的子集,以使每条边都连通一个子集中的一个节点与另一个子集中的一个节点。

示例 2:

img

输入:graph = [[1,3],[0,2],[1,3],[0,2]]
输出:true
解释:可以将节点分成两组: {0, 2} 和 {1, 3}。

# 🔔 解题思路

基本思路,为一条边的两个节点分别涂上不同的颜色,如果任意一条边的两个节点都能被涂上不同的颜色(用 0 和 1 标记为两种颜色,另外,未涂色的节点标记为 -1),那么整个图就是一个二分图。

class Solution {

    private static final int NO_COLOR = -1;
    private static final int COLOR_ONE = 0;
    private static final int COLOR_TWO = 1;

    public boolean isBipartite(int[][] graph) {
        // 图中节点的个数
        int size = graph.length;
        int[] colors = new int[size];
        // 未涂色的节点标记为 -1;
        Arrays.fill(colors, NO_COLOR); 

        for (int i = 0; i < size; i ++) {
            if (colors[i] == NO_COLOR) {
                // setColor 表示对以 graph[i] 为起点的连通图进行着色,如果能够按照二分图的规则进行着色,则返回 true
                if (!setColor(graph, colors, i)) {
                    return false;
                }
            }
        }

        return true;
    }

    // 对以 graph[i] 为起点的连通图进行着色,如果能够按照二分图的规则对其进行着色,则返回 true
    private boolean setColor(int[][] graph, int[] colors, int i) {
        ......
    }
}

# DFS

连通性问题,DFS 做效率更高

# 递归

代码配合图片食用:

class Solution {

    private static final int NO_COLOR = -1;
    private static final int COLOR_ONE = 0;
    private static final int COLOR_TWO = 1;

    public boolean isBipartite(int[][] graph) {
        // 图中节点的个数
        int size = graph.length;
        int[] colors = new int[size];
        // 未涂色的节点标记为 -1;
        Arrays.fill(colors, NO_COLOR); 

        for (int i = 0; i < size; i ++) {
            if (colors[i] == NO_COLOR) {
                // 对以 graph[i] 为起点的连通图进行着色(将节点 i 的颜色设为 COLOR_ONE),如果能够按照二分图的规则对其进行着色,则返回 true
                if (!setColor(graph, colors, i, COLOR_ONE)) {
                    return false;
                }
            }
        }

        return true;
    }
	
    // 对以 graph[i] 为起点的连通图进行着色(将节点 i 的颜色设为 color),如果能够按照二分图的规则对其进行着色,则返回 true  
    private boolean setColor(int[][] graph, int[] colors, int i, int color) {
        // 1. 如果该节点在此之前已经着色,并且它的颜色不是 color,那么意味着不能按照二分图的规则对图中的节点进行着色,返回 false
        if (colors[i] != NO_COLOR) {
            return colors[i] == color;
        }
        
        // 2. 如果此时节点 i 还没有着色,则将它的颜色设为 color,然后给与它相邻的节点涂上第二种颜色(1 - color)
        colors[i] = color;
        // 给相邻节点着色
        for (int neighbor : graph[i]) {
            if (!setColor(graph, colors, neighbor, 1-color)) {
                return false;
            }
        }    

        return true;
    }
}

# 迭代

// 对以 graph[i] 为起点的连通图进行着色,如果能够按照二分图的规则进行着色,则返回 true
private boolean setColor(int[][] graph, int[] colors, int i) {
    Stack<Integer> stack = new Stack<>();
    stack.push(i);
    colors[i] = COLOR_ONE;

    while (!stack.isEmpty()) {
        int size = stack.size();
        for (int k = 0; k < size; k ++) {
            int cur = stack.pop();
            for (int neighbor : graph[cur]) {
                // 如果相邻节点已经着色
                if (colors[neighbor] != NO_COLOR) {
                    // 相邻节点和当前节点同色,则返回 false
                    if (colors[cur] == colors[neighbor]) {
                        return false;
                    }
                }
                // 如果相邻节点没有着色,则按照二分图规则进行着色
                else {
                    stack.push(neighbor);
                    colors[neighbor] = (colors[cur] == COLOR_ONE) ? COLOR_TWO : COLOR_ONE;
                }
            }    
        }
    }

    return true;
}

# BFS

// 对以 graph[i] 为起点的连通图进行着色,如果能够按照二分图的规则进行着色,则返回 true
private boolean setColor(int[][] graph, int[] colors, int i) {
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(i);
    colors[i] = COLOR_ONE;

    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int k = 0; k < size; k ++) {
            int cur = queue.poll();
            for (int neighbor : graph[cur]) {
                // 如果相邻节点已经着色
                if (colors[neighbor] != NO_COLOR) {
                    // 相邻节点和当前节点同色,则返回 false
                    if (colors[cur] == colors[neighbor]) {
                        return false;
                    }
                }
                // 如果相邻节点没有着色,则按照二分图规则进行着色
                else {
                    queue.offer(neighbor);
                    colors[neighbor] = (colors[cur] == COLOR_ONE) ? COLOR_TWO : COLOR_ONE;
                }
            }    
        }

    }

    return true;
}

# 💥 复杂度分析

  • 空间复杂度:
  • 时间复杂度:

🎁 公众号

各位小伙伴大家好呀,叫我小牛肉就行,目前在读东南大学硕士,上方扫码关注公众号「飞天小牛肉」,与你分享我的成长历程与技术感悟~

帮助小牛肉改善此页面 (opens new window)
Last Updated: 2023/02/16, 11:27:10
节点间通路
所有路径:需要存储路径的 DFS 模板

← 节点间通路 所有路径:需要存储路径的 DFS 模板→

最近更新
01
我掏出这个多线程轮子,面试官直接全体起立
04-13
02
面试官问你有其他 Offer 吗,该怎样回答
04-10
03
天不生陈志龙,职场万古如长夜
04-06
更多文章>
Theme by Vdoing | Copyright © 2019-2023 飞天小牛肉 | 皖ICP备2022008966号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式