题目
给定一个无向图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]里边。
解题思路
BFS/DFS
一开始没什么思路,大概瞟了一眼题解,然后做出来的。
深度优先搜索,每次访问到1个节点时,把节点周围的邻居节点压入栈,并且根据该节点的颜色,给邻居节点上其他色。如果栈pop出的节点已经被访问过,则查看节点被涂的色和在栈内备选的色是否相同,如果不同,就说明有节点冲突了
需要注意的是,图可能是不联通的,从1个节点可能无法访问到所有节点,所以每次把栈内元素都访问完后,需要看看是否还剩下节点没走过,然后再从没有访问过的节点中挑一个重新开始深搜
Or bfs, if the node is already visited, then continue. Otherwise do a bfs starting with this node.
Union Find Set
Use a union find set, union the neighbors to the first neighbor node, return False
if the current node has the same parent as the neighbor node.
Time complexity:
o
(
n
log
n
)
o(n\log n)
o(nlogn)
Space complexity:
o
(
n
)
o(n)
o(n)
代码
DFS
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
visited_nodes = set()
all_nodes = set([index for index in range(len(graph)) if graph[index]])
while all_nodes - visited_nodes:
stack = [(list(all_nodes - visited_nodes)[0], 'a')]
cluster = {'a': [], 'b': []}
while stack:
node, node_cluster = stack.pop()
if node not in visited_nodes:
visited_nodes.add(node)
cluster[node_cluster].append(node)
if node_cluster == 'a':
neighbor_cluster = 'b'
else:
neighbor_cluster = 'a'
for neighbor in graph[node]:
stack.append((neighbor, neighbor_cluster))
else:
if node not in cluster[node_cluster]:
return False
return True
BFS
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
visited = {}
for i in range(len(graph)):
if i in visited:
continue
queue = collections.deque([(i, 1)])
while queue:
node, color = queue.popleft()
if node in visited and color != visited[node]:
return False
elif node in visited:
continue
visited[node] = color
for neighbor_node in graph[node]:
queue.append((neighbor_node, -1 * color))
return True
Union Find Set
class Solution:
class UnionFindSet:
def __init__(self, n: int) -> None:
self.parent = [i for i in range(n)]
self.height = [1] * n
def find(self, x: int) -> int:
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x: int, y: int) -> None:
x, y = self.find(x), self.find(y)
if x == y:
return
if self.height[x] > self.height[y]:
self.parent[y] = x
else:
self.parent[y] = x
if self.height[x] == self.height[y]:
self.height[x] += 1
def isBipartite(self, graph: List[List[int]]) -> bool:
n = len(graph)
ufs = self.UnionFindSet(n)
for i in range(n):
for neighbor_i in range(len(graph[i])):
if ufs.find(i) == ufs.find(graph[i][neighbor_i]):
return False
ufs.union(graph[i][neighbor_i], graph[i][0])
return True