題目:
給定一個無向圖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<Integer> 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)