题目:
给定一个无向图graph,当这个图为二分图时返回true。
如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。
graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。
示例 1:
输入: [[1,3], [0,2], [1,3], [0,2]]
输出: true
解释:
无向图如下:
0----1
| |
| |
3----2
我们可以将节点分成两组: {0, 2} 和 {1, 3}。
示例 2:
输入: [[1,2,3], [0,2], [0,1,3], [0,2]]
输出: false
解释:
无向图如下:
0----1
| \ |
| \ |
3----2
我们不能将节点分割成两个独立的子集。
注意:
graph 的长度范围为 [1, 100]。
graph[i] 中的元素的范围为 [0, graph.length - 1]。
graph[i] 不会包含 i 或者有重复的值。
图是无向的: 如果j 在 graph[i]里边, 那么 i 也会在 graph[j]里边。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/is-graph-bipartite
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:
二分图是离散数学中图论章节的内容。二分图又称二部图,即图(这里指无向图)中的的点,可以划分为两个部分。划分的规则为:连线两端的点分别属于两个部其中的一个。称这样的图为二分图。
在解决该问题时,使用的是染色法。即给点打标签。
而打标签的规则为:如果当前点还没有被打上标签,则给其打上与其所有邻点不同的标签;如果当前点已经打上标签则continue。
在实现的时候使用1,2,3三种数字来作为“染剂”染色。
由于操作对象是图,并且需要遍历,所以自然的想到DFS(深度优先搜索)和BFS(广度优先搜索)。
而由于操作对象 图 不一定是连通图,所以可能有多个连通分量,这时一次DFS或者BFS就不够,需要使用一个外循环来控制DFS/BFS的入口:
for(int i = 0;i < lens;i++){
if(book[i] == 1) continue;
book[i] = 1;
dfs(graph, paint, book, i);
}
最后,通常的,实现DFS与BFS时都需要使用一个标签book数组来记录已经遍历过的节点。
再由于本题需要给节点打标签,所以额外需要paint数组记录每一个节点的分组情况。
实现:
JAVA版:
package leetcode;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
/*
USER:LQY
DATE:2020/7/16
TIME:8:34
*/
public class leetcode_785 {
public static void main(String []args){
int [][]graph = {
{},
{2,4,6},
{1,4,8,9},
{7,8},
{1,2,8,9},
{6,9},
{1,5,7,8,9},
{3,6,9},
{2,3,4,6,9},
{2,4,5,6,7,8}
};
System.out.println("\n"+new leetcode_785().isBipartite(graph));
}
public boolean isBipartite(int[][] graph) {
int lens = graph.length;
int []paint = new int[lens];
int []book = new int[lens];
// DFS
// for(int i = 0;i < lens;i++){
// if(book[i] == 1) continue;
// book[i] = 1;
// dfs(graph, paint, book, i);
// }
//BFS
// LinkedList queen = new LinkedList<>();
// for(int k = 0;k < lens;k++){
// if(book[k] == 1) continue;
// queen.addLast(k);
// while(!queen.isEmpty()){
// int nv = queen.removeFirst();
//// book[nv] = 1;
// //添加邻边的同时染色
// int f1 = 0;
// int f2 = 0;
//// int f3 = 0;
// for(int i = 0;i < graph[nv].length;i++){
// int next = graph[nv][i];
// if(paint[next] == 1) f1 = 1;
// if(paint[next] == 2) f2 = 2;
//// if(paint[i] == 3) f3 = 3;
// if(book[next] == 1) continue;
// queen.addLast(next);
// book[next] = 1;
//
// }
// //染色
// if(f1 == 0){
// paint[nv] = 1;
// }else if(f2 == 0){
// paint[nv] = 2;
// }else{
// paint[nv] = 3;
// }
// System.out.println("点"+nv+"被染成:"+paint[nv]);
// }
// }
for(int i : paint){
System.out.print(i+" ");
}
for(int i = 0;i < lens;i++){
if(paint[i] == 3){
return false;
}
}
return true;
}
public void dfs(int [][]graph, int []paint, int[]book, int nv) {
if(paint[nv] != 0) return;
//先判断该点处是否染色
int f1 = 0;
int f2 = 0;
// int f3 = 0;
//统计邻边染色情况
for(int i = 0;i < graph[nv].length;i++){
int next = graph[nv][i];
if(paint[next] == 1) f1 = 1;
if(paint[next] == 2) f2 = 2;
// if(paint[i] == 3) f3 = 3;
}
//染色
if(f1 == 0){
paint[nv] = 1;
}else if(f2 == 0){
paint[nv] = 2;
}else{
paint[nv] = 3;
}
System.out.println("点"+nv+"被染成:"+paint[nv]);
//深搜
for(int i = 0;i < graph[nv].length;i++){
int next = graph[nv][i];
if(book[next] == 1) continue;
book[next] = 1;
dfs(graph, paint, book, next);
}
}
}
Python版:
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
lens = len(graph)
paint = [0 for _ in range(lens)]
book = [0 for _ in range(lens)]
for i in range(lens):
if(book[i] > 0):
continue
book[i] = 1
self.dfs(graph, paint, book, i)
for i in range(lens):
if(paint[i] == 3):
return False
return True
def dfs(self, graph: List[List[int]], paint: List[int], book: List[int], nv: int):
if(paint[nv] > 0):
return
f1 = 0
f2 = 0
for i in range(0, len(graph[nv])):
next = graph[nv][i]
if(paint[next] == 1):
f1 = 1
if(paint[next] == 2):
f2 = 2
if(f1 == 0):
paint[nv] = 1
elif(f2 == 0):
paint[nv] = 2
else:
paint[nv] = 3
for i in range(0, len(graph[nv])):
next = graph[nv][i]
if(book[next] == 1):
continue
book[next] = 1
self.dfs(graph, paint, book, next)