描述

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

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

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

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

示例

示例 1:
bi2.jpg

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

示例 2:
bi1.jpg

  1. 输入:graph = [[1,3],[0,2],[1,3],[0,2]]
  2. 输出:true
  3. 解释:可以将节点分成两组: {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] 的所有值 互不相同
  • 如果 graph[u] 包含 v,那么 graph[v] 也会包含 u

解题思路

1、深度优先搜索 / 广度优先搜索

我们使用图搜索算法从各个连通域的任一顶点开始遍历整个连通域,遍历的过程中用两种不同的颜色对顶点进行染色,相邻顶点染成相反的颜色。这个过程中倘若发现相邻的顶点被染成了相同的颜色,说明它不是二分图;反之,如果所有的连通域都染色成功,说明它是二分图。

广度优先搜索代码

  1. class Solution {
  2. public boolean isBipartite(int[][] graph) {
  3. // 定义 visited 数组,初始值为 0 表示未被访问,赋值为 1 或者 -1 表示两种不同的颜色。
  4. int[] visited = new int[graph.length];
  5. Queue<Integer> queue = new LinkedList<>();
  6. // 因为图中可能含有多个连通域,所以我们需要判断是否存在顶点未被访问,若存在则从它开始再进行一轮 bfs 染色。
  7. for (int i = 0; i < graph.length; i++) {
  8. if (visited[i] != 0) {
  9. continue;
  10. }
  11. // 每出队一个顶点,将其所有邻接点染成相反的颜色并入队。
  12. queue.offer(i);
  13. visited[i] = 1;
  14. while (!queue.isEmpty()) {
  15. int v = queue.poll();
  16. for (int w: graph[v]) {
  17. // 如果当前顶点的某个邻接点已经被染过色了,且颜色和当前顶点相同,说明此无向图无法被正确染色,返回 false。
  18. if (visited[w] == visited[v]) {
  19. return false;
  20. }
  21. if (visited[w] == 0) {
  22. visited[w] = -visited[v];
  23. queue.offer(w);
  24. }
  25. }
  26. }
  27. }
  28. return true;
  29. }
  30. }

深度优先搜索代码

  1. class Solution {
  2. private static final int UNCOLORED = 0;
  3. private static final int RED = 1;
  4. private static final int GREEN = 2;
  5. private int[] color;
  6. private boolean valid;
  7. public boolean isBipartite(int[][] graph) {
  8. int n = graph.length;
  9. valid = true;
  10. color = new int[n];
  11. Arrays.fill(color, UNCOLORED);
  12. for (int i = 0; i < n && valid; ++i) {
  13. if (color[i] == UNCOLORED) {
  14. dfs(i, RED, graph);
  15. }
  16. }
  17. return valid;
  18. }
  19. public void dfs(int node, int c, int[][] graph) {
  20. color[node] = c;
  21. int cNei = c == RED ? GREEN : RED;
  22. for (int neighbor : graph[node]) {
  23. if (color[neighbor] == UNCOLORED) {
  24. dfs(neighbor, cNei, graph);
  25. if (!valid) {
  26. return;
  27. }
  28. } else if (color[neighbor] != cNei) {
  29. valid = false;
  30. return;
  31. }
  32. }
  33. }
  34. }