785 判断二分图
题目
给定一个无向图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
我们不能将节点分割成两个独立的子集。
方法一(深度优先搜索)
对图进行DFS,在遍历的过程中用两种颜色对节点进行染色,其中相邻的节点要染成不同的颜色。如果在遍历的过程中发现相邻的节点颜色相同,那么它不是二分图。如果用这样的染色策略对所有节点染色成功,那么它是二分图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| int[] visited; public boolean isBipartite(int[][] graph) { visited = new int[graph.length]; for(int i = 0; i < graph.length; i++){ if(visited[i] == 0){ if(!dfs(graph, i, 1)) return false; } } return true; }
public boolean dfs(int[][] graph, int i, int color){ if(visited[i] != 0) return visited[i] == color; visited[i] = color; for(int j : graph[i]){ if(!dfs(graph, j, -color)) return false; } return true; }
|
方法二(广度优先遍历)
思路和方法一相同,只是遍历的方法从DFS改成了BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public boolean isBipartite(int[][] graph) { int[] visited = new int[graph.length]; for(int i = 0; i < graph.length; i++){ if(visited[i] != 0) continue; Queue<Integer> queue = new LinkedList<>(); queue.add(i); visited[i] = 1; while(!queue.isEmpty()){ int cur = queue.poll(); for(int neighbor : graph[cur]){ if(visited[neighbor] == visited[cur]) return false; if(visited[neighbor] == 0){ visited[neighbor] = -visited[cur]; queue.add(neighbor); } } } } return true; }
|
方法三(并查集)
在一个二分图中,一个节点的所有邻居都具有相同的颜色,并且和这个节点颜色不同。
因此我们如果用并查集来看这个问题,则上面一句话可以翻译如下:在二分图中,一个节点的所有邻居都在相同的集合中,并且和这个节点所在的集合不同。
我们可以遍历所有节点,当遍历到一个节点时,先判断它是否有邻居节点和它已经在一个集合中了(find),如果有则不是二分图,直接返回false,否则将它的所有邻居节点所在的集合合并(union),再接着遍历下一个节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| class Solution { public boolean isBipartite(int[][] graph) { UnionFindSet ufs = new UnionFindSet(); List<Integer> nodes = new LinkedList<>(); for(int i = 0; i < graph.length; i++) nodes.add(i); ufs.makeSets(nodes); for(int i = 0; i < graph.length; i++){ int cur = i; int[] neighbors = graph[cur]; for(int neighbor : neighbors){ if(ufs.findHead(neighbor) == ufs.findHead(cur)) return false; ufs.union(neighbor, neighbors[0]); } } return true; }
public static class UnionFindSet { public HashMap<Integer, Integer> fatherMap; public HashMap<Integer, Integer> sizeMap;
public UnionFindSet() { fatherMap = new HashMap<Integer, Integer>(); sizeMap = new HashMap<Integer, Integer>(); }
public void makeSets(List<Integer> nodes) { for (int node : nodes) { fatherMap.put(node, node); sizeMap.put(node, 1); } } private int findHead(int node) { int father = fatherMap.get(node); if (father != node) { father = findHead(father); } fatherMap.put(node, father); return father; }
public void union(int a, int b) { int aHead = findHead(a); int bHead = findHead(b); if (aHead != bHead) { int aSetSize= sizeMap.get(aHead); int bSetSize = sizeMap.get(bHead); if (aSetSize <= bSetSize) { fatherMap.put(aHead, bHead); sizeMap.put(bHead, aSetSize + bSetSize); } else { fatherMap.put(bHead, aHead); sizeMap.put(aHead, aSetSize + bSetSize); } } } } }
|
参考