LC 0785 [M] Is Graph Bipartite? - ALawliet/algorithms GitHub Wiki
for BFS: draw it as a tree in layers and check if we can find a cycle/cross edge that specifically occurs on the same level layer, if so it is NOT bipartite
class Solution:
def isBipartite(self, G: List[List[int]]) -> bool:
n = len(G)
visited = set()
level = dict()
def bfs_detected_cycle_crossedge(i):
Q = deque([i])
visited.add(i)
level[i] = 0 #
while Q:
u = Q.popleft()
for v in G[u]: # neighbors
if v not in visited:
visited.add(v)
level[v] = level[u] + 1 #
Q.append(v)
else:
if level[v] == level[u]: return True # same level has a cross edge
return False
for i in range(n):
if i not in visited:
if bfs_detected_cycle_crossedge(i): return False
return True
"pair applicants to the most jobs, pair stable marriage"
for DFS: is it possible to color a graph in 2 colors such that adjacent vertices are different colors?
a graph is not bipartite if we detect a single odd-length cycle starting from some v on any connected component: => a cross edge on the same layer (level distance) cross edges on different adj layers are ok
if using DFS back edges that go back to the same level to create odd-length cycles means it is not bipartite
from queue import Queue
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
n = len(graph)
# for this problem, they give you the adj list of neighbors directly, so don't need to create the adj map
def bfs(v):
Q = Queue()
Q.put(v)
visited.add(v)
distance[v] = 0
while not Q.empty():
node = Q.get()
for neighbor in graph[node]:
if neighbor not in visited:
# TREE EDGE
visited.add(neighbor)
distance[neighbor] = 1 + distance[node]
parent[neighbor] = node
Q.put(neighbor)
else:
if neighbor != parent[node]:
# CROSS EDGE = cycle detected
if distance[neighbor] == distance[node]:
# same-level cross edge = odd-length cycle = not bipartite
return False
# all cross edges were between vertices in adj layers, so it is bipartite
return True
def dfs(node):
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
color[neighbor] = 1 - color[node]
# TREE EDGE
parent[neighbor] = node
if dfs(neighbor) == False: return False
# current parent or some ancestor further up could be a BACK EDGE
# => there was a cycle detected in a previous subtree
else:
if node in parent: # now needed for some reason
if neighbor != parent[node]:
# BACK EDGE = cycle detected
if color[neighbor] == color[node]: # ***
# back edge that creates odd-length cycle
return False
return True
visited = set()
parent = dict() # must track the parent to detect cycles: BFS (cross edges), DFS (back edges)
num_components = 0
distance = dict() # BFS ***
color = {v: 0 for v in range(n)} # DFS ***
is_bipartite = bool()
# we must still explore all of the components starting from some v
# because if ANY connected component is not bipartite, then the graph cannot be bipartite
# you might have cases where 1 component is bipartite but the next is not
# for v in range(n):
# if v not in visited:
# # number_of_connected_components += 1
# # if number_of_connected_components > 1: return False
# if (bfs(v) == False): is_bipartite = False
is_bipartite = not any(bfs(v) == False for v in range(n) if v not in visited)
# is_bipartite = not any(dfs(v) == False for v in range(n) if v not in visited)
return is_bipartite