Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using union-find strategy.
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.Problem summary: 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: There are no self-edges (graph[u] does not contain u). There are no parallel edges (graph[u] does not contain duplicate values). If v is in graph[u], then u is in graph[v] (the graph is undirected). The graph may not be connected, meaning there may be two nodes 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Union-Find
[[1,2,3],[0,2],[0,1,3],[0,2]]
[[1,3],[0,2],[1,3],[0,2]]
divide-nodes-into-the-maximum-number-of-groups)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #785: Is Graph Bipartite?
class Solution {
private int[] color;
private int[][] g;
public boolean isBipartite(int[][] graph) {
int n = graph.length;
color = new int[n];
g = graph;
for (int i = 0; i < n; ++i) {
if (color[i] == 0 && !dfs(i, 1)) {
return false;
}
}
return true;
}
private boolean dfs(int a, int c) {
color[a] = c;
for (int b : g[a]) {
if (color[b] == c || (color[b] == 0 && !dfs(b, -c))) {
return false;
}
}
return true;
}
}
// Accepted solution for LeetCode #785: Is Graph Bipartite?
func isBipartite(graph [][]int) bool {
n := len(graph)
color := make([]int, n)
var dfs func(int, int) bool
dfs = func(a, c int) bool {
color[a] = c
for _, b := range graph[a] {
if color[b] == c || (color[b] == 0 && !dfs(b, -c)) {
return false
}
}
return true
}
for i := range graph {
if color[i] == 0 && !dfs(i, 1) {
return false
}
}
return true
}
# Accepted solution for LeetCode #785: Is Graph Bipartite?
class Solution:
def isBipartite(self, graph: List[List[int]]) -> bool:
def dfs(a: int, c: int) -> bool:
color[a] = c
for b in graph[a]:
if color[b] == c or (color[b] == 0 and not dfs(b, -c)):
return False
return True
n = len(graph)
color = [0] * n
for i in range(n):
if color[i] == 0 and not dfs(i, 1):
return False
return True
// Accepted solution for LeetCode #785: Is Graph Bipartite?
impl Solution {
pub fn is_bipartite(graph: Vec<Vec<i32>>) -> bool {
let n = graph.len();
let mut color = vec![0; n];
fn dfs(a: usize, c: i32, graph: &Vec<Vec<i32>>, color: &mut Vec<i32>) -> bool {
color[a] = c;
for &b in &graph[a] {
if color[b as usize] == c
|| (color[b as usize] == 0 && !dfs(b as usize, -c, graph, color))
{
return false;
}
}
true
}
for i in 0..n {
if color[i] == 0 && !dfs(i, 1, &graph, &mut color) {
return false;
}
}
true
}
}
// Accepted solution for LeetCode #785: Is Graph Bipartite?
function isBipartite(graph: number[][]): boolean {
const n = graph.length;
const color: number[] = Array(n).fill(0);
const dfs = (a: number, c: number): boolean => {
color[a] = c;
for (const b of graph[a]) {
if (color[b] === c || (color[b] === 0 && !dfs(b, -c))) {
return false;
}
}
return true;
};
for (let i = 0; i < n; i++) {
if (color[i] === 0 && !dfs(i, 1)) {
return false;
}
}
return true;
}
Use this to step through a reusable interview workflow for this problem.
Track components with a list or adjacency matrix. Each union operation may need to update all n elements’ component labels, giving O(n) per union. For n union operations total: O(n²). Find is O(1) with direct lookup, but union dominates.
With path compression and union by rank, each find/union operation takes O(α(n)) amortized time, where α is the inverse Ackermann function — effectively constant. Space is O(n) for the parent and rank arrays. For m operations on n elements: O(m × α(n)) total.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.