There is an undirected graph with n nodes, where each node is numbered between 0 and n - 1. You are given a 2D array graph, where graph[u] is an array of nodes that node u is adjacent to. More formally, for each v in graph[u], there is an undirected edge between node u and node v. The graph has the following properties:
graph[u] does not contain u).graph[u] does not contain duplicate values).v is in graph[u], then u is in graph[v] (the graph is undirected).u and v such that there is no path between them.A graph is bipartite if the nodes can be partitioned into two independent sets A and B such that every edge in the graph connects a node in set A and a node in set B.
Return true if and only if it is bipartite.
Example 1:
Input: graph = [[1,2,3],[0,2],[0,1,3],[0,2]] Output: false Explanation: There is no way to partition the nodes into two independent sets such that every edge connects a node in one and a node in the other.
Example 2:
Input: graph = [[1,3],[0,2],[1,3],[0,2]]
Output: true
Explanation: We can partition the nodes into two sets: {0, 2} and {1, 3}.
Constraints:
graph.length == n1 <= n <= 1000 <= graph[u].length < n0 <= graph[u][i] <= n - 1graph[u] does not contain u.graph[u] are unique.graph[u] contains v, then graph[v] contains u.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to determining if a graph is bipartite involves trying every possible way to color the graph using only two colors. We systematically explore each combination to see if any of them satisfy the bipartite property.
Here's how the algorithm would work step-by-step:
def is_graph_bipartite_brute_force(graph):
number_of_nodes = len(graph)
# Try every possible coloring of the graph with two colors.
for i in range(2**number_of_nodes):
coloring = {}
# Assign colors based on the current bitmask 'i'.
for node_index in range(number_of_nodes):
if (i >> node_index) & 1:
coloring[node_index] = 0 # Color 0
else:
coloring[node_index] = 1 # Color 1
is_bipartite = True
# Check if the current coloring is valid.
for node in range(number_of_nodes):
#Iterate to all neighbors of a given node
for neighbor in graph[node]:
#Check color differences.
if node in coloring and neighbor in coloring and coloring[node] == coloring[neighbor]:
is_bipartite = False
break
if not is_bipartite:
break
#If any coloring is bipartite, we're done
if is_bipartite:
return True
return FalseWe can efficiently determine if a graph is bipartite by systematically coloring the nodes. If we encounter a conflict in coloring, the graph isn't bipartite; otherwise, it is. We don't need to check every possible arrangement, making it faster than brute force.
Here's how the algorithm would work step-by-step:
def is_bipartite(graph):
number_of_nodes = len(graph)
node_colors = [0] * number_of_nodes
def bfs(start_node):
queue = [start_node]
node_colors[start_node] = 1 # Assign the starting node a color.
while queue:
current_node = queue.pop(0)
for neighbor_node in graph[current_node]:
if node_colors[neighbor_node] == 0:
node_colors[neighbor_node] = -node_colors[current_node]
# Add to the queue for further exploration.
queue.append(neighbor_node)
elif node_colors[neighbor_node] == node_colors[current_node]:
return False # Conflict: not bipartite.
return True
# Handle disconnected graphs.
for node in range(number_of_nodes):
if node_colors[node] == 0:
if not bfs(node):
return False
# If no conflicts were found, the graph is bipartite.
return True| Case | How to Handle |
|---|---|
| Empty graph (graph with no nodes) | Return true because an empty graph is considered bipartite. |
| Graph with only one node (isolated node) | Return true as a single node graph is trivially bipartite. |
| Disconnected graph (multiple components) | Iterate through each node, and if uncolored, start a BFS/DFS to color that component. |
| Graph with a self-loop (node connected to itself) | If a node has a self-loop, the coloring logic during BFS/DFS will detect the conflict and return false. |
| Complete graph (every node connected to every other node) | A complete graph with more than 2 nodes is not bipartite; the algorithm should correctly identify and return false. |
| Large graph with many nodes and edges, potential stack overflow with recursion | Use iterative BFS instead of recursive DFS to avoid stack overflow issues on large graphs. |
| Graph with only two nodes connected to each other | The algorithm should correctly color each node with a different color and return true. |
| A bipartite graph with nodes labeled with large numbers | The coloring algorithm works independent of actual node values, focusing on connectivity thus large node numbers pose no issue. |