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 core interview patterns strategy.
You are given an integer n, the number of nodes in a directed graph where the nodes are labeled from 0 to n - 1. Each edge is red or blue in this graph, and there could be self-edges and parallel edges.
You are given two arrays redEdges and blueEdges where:
redEdges[i] = [ai, bi] indicates that there is a directed red edge from node ai to node bi in the graph, andblueEdges[j] = [uj, vj] indicates that there is a directed blue edge from node uj to node vj in the graph.Return an array answer of length n, where each answer[x] is the length of the shortest path from node 0 to node x such that the edge colors alternate along the path, or -1 if such a path does not exist.
Example 1:
Input: n = 3, redEdges = [[0,1],[1,2]], blueEdges = [] Output: [0,1,-1]
Example 2:
Input: n = 3, redEdges = [[0,1]], blueEdges = [[2,1]] Output: [0,1,-1]
Constraints:
1 <= n <= 1000 <= redEdges.length, blueEdges.length <= 400redEdges[i].length == blueEdges[j].length == 20 <= ai, bi, uj, vj < nProblem summary: You are given an integer n, the number of nodes in a directed graph where the nodes are labeled from 0 to n - 1. Each edge is red or blue in this graph, and there could be self-edges and parallel edges. You are given two arrays redEdges and blueEdges where: redEdges[i] = [ai, bi] indicates that there is a directed red edge from node ai to node bi in the graph, and blueEdges[j] = [uj, vj] indicates that there is a directed blue edge from node uj to node vj in the graph. Return an array answer of length n, where each answer[x] is the length of the shortest path from node 0 to node x such that the edge colors alternate along the path, or -1 if such a path does not exist.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: General problem-solving
3 [[0,1],[1,2]] []
3 [[0,1]] [[2,1]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1129: Shortest Path with Alternating Colors
class Solution {
public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) {
List<Integer>[][] g = new List[2][n];
for (var f : g) {
Arrays.setAll(f, k -> new ArrayList<>());
}
for (var e : redEdges) {
g[0][e[0]].add(e[1]);
}
for (var e : blueEdges) {
g[1][e[0]].add(e[1]);
}
Deque<int[]> q = new ArrayDeque<>();
q.offer(new int[] {0, 0});
q.offer(new int[] {0, 1});
boolean[][] vis = new boolean[n][2];
int[] ans = new int[n];
Arrays.fill(ans, -1);
int d = 0;
while (!q.isEmpty()) {
for (int k = q.size(); k > 0; --k) {
var p = q.poll();
int i = p[0], c = p[1];
if (ans[i] == -1) {
ans[i] = d;
}
vis[i][c] = true;
c ^= 1;
for (int j : g[c][i]) {
if (!vis[j][c]) {
q.offer(new int[] {j, c});
}
}
}
++d;
}
return ans;
}
}
// Accepted solution for LeetCode #1129: Shortest Path with Alternating Colors
func shortestAlternatingPaths(n int, redEdges [][]int, blueEdges [][]int) []int {
g := [2][][]int{}
for i := range g {
g[i] = make([][]int, n)
}
for _, e := range redEdges {
g[0][e[0]] = append(g[0][e[0]], e[1])
}
for _, e := range blueEdges {
g[1][e[0]] = append(g[1][e[0]], e[1])
}
type pair struct{ i, c int }
q := []pair{pair{0, 0}, pair{0, 1}}
ans := make([]int, n)
vis := make([][2]bool, n)
for i := range ans {
ans[i] = -1
}
d := 0
for len(q) > 0 {
for k := len(q); k > 0; k-- {
p := q[0]
q = q[1:]
i, c := p.i, p.c
if ans[i] == -1 {
ans[i] = d
}
vis[i][c] = true
c ^= 1
for _, j := range g[c][i] {
if !vis[j][c] {
q = append(q, pair{j, c})
}
}
}
d++
}
return ans
}
# Accepted solution for LeetCode #1129: Shortest Path with Alternating Colors
class Solution:
def shortestAlternatingPaths(
self, n: int, redEdges: List[List[int]], blueEdges: List[List[int]]
) -> List[int]:
g = [defaultdict(list), defaultdict(list)]
for i, j in redEdges:
g[0][i].append(j)
for i, j in blueEdges:
g[1][i].append(j)
ans = [-1] * n
vis = set()
q = deque([(0, 0), (0, 1)])
d = 0
while q:
for _ in range(len(q)):
i, c = q.popleft()
if ans[i] == -1:
ans[i] = d
vis.add((i, c))
c ^= 1
for j in g[c][i]:
if (j, c) not in vis:
q.append((j, c))
d += 1
return ans
// Accepted solution for LeetCode #1129: Shortest Path with Alternating Colors
struct Solution;
use std::collections::VecDeque;
impl Solution {
fn shortest_alternating_paths(
n: i32,
red_edges: Vec<Vec<i32>>,
blue_edges: Vec<Vec<i32>>,
) -> Vec<i32> {
let n = n as usize;
let mut graph: Vec<Vec<Vec<usize>>> = vec![vec![vec![]; n]; 2];
let mut visited: Vec<Vec<bool>> = vec![vec![false; n]; 2];
let mut res: Vec<i32> = vec![-1; n];
for e in red_edges {
let u = e[0] as usize;
let v = e[1] as usize;
graph[0][u].push(v);
}
for e in blue_edges {
let u = e[0] as usize;
let v = e[1] as usize;
graph[1][u].push(v);
}
let mut queue: VecDeque<(usize, usize, usize)> = VecDeque::new();
queue.push_back((0, 0, 0));
queue.push_back((0, 1, 0));
visited[0][0] = true;
visited[1][0] = true;
while let Some((u, c, d)) = queue.pop_front() {
if res[u] == -1 {
res[u] = d as i32;
}
for &v in &graph[c][u] {
if !visited[1 - c][v] {
visited[1 - c][v] = true;
queue.push_back((v, 1 - c, d + 1));
}
}
}
res
}
}
#[test]
fn test() {
let n = 3;
let red_edges = vec_vec_i32![[0, 1], [1, 2]];
let blue_edges = vec_vec_i32![];
let res = vec![0, 1, -1];
assert_eq!(
Solution::shortest_alternating_paths(n, red_edges, blue_edges),
res
);
let n = 3;
let red_edges = vec_vec_i32![[0, 1]];
let blue_edges = vec_vec_i32![[2, 1]];
let res = vec![0, 1, -1];
assert_eq!(
Solution::shortest_alternating_paths(n, red_edges, blue_edges),
res
);
let n = 3;
let red_edges = vec_vec_i32![[1, 0]];
let blue_edges = vec_vec_i32![[2, 1]];
let res = vec![0, -1, -1];
assert_eq!(
Solution::shortest_alternating_paths(n, red_edges, blue_edges),
res
);
let n = 3;
let red_edges = vec_vec_i32![[0, 1]];
let blue_edges = vec_vec_i32![[1, 2]];
let res = vec![0, 1, 2];
assert_eq!(
Solution::shortest_alternating_paths(n, red_edges, blue_edges),
res
);
let n = 3;
let red_edges = vec_vec_i32![[0, 1], [0, 2]];
let blue_edges = vec_vec_i32![[1, 0]];
let res = vec![0, 1, 1];
assert_eq!(
Solution::shortest_alternating_paths(n, red_edges, blue_edges),
res
);
}
// Accepted solution for LeetCode #1129: Shortest Path with Alternating Colors
function shortestAlternatingPaths(
n: number,
redEdges: number[][],
blueEdges: number[][],
): number[] {
const g: [Graph, Graph] = [{}, {}];
const ans = Array(n).fill(-1);
const vis = Array.from({ length: n }, () => Array.from({ length: 2 }, () => false));
let q: Vertex[] = [
[0, 0],
[0, 1],
];
vis[0][0] = vis[0][1] = true;
let d = 0;
for (const [i, j] of redEdges) {
(g[0][i] ??= []).push(j);
}
for (const [i, j] of blueEdges) {
(g[1][i] ??= []).push(j);
}
while (q.length) {
const qNext: Vertex[] = [];
for (let [i, color] of q) {
if (ans[i] === -1) {
ans[i] = d;
}
color ^= 1;
for (const j of g[color][i] ?? []) {
if (!vis[j][color]) {
vis[j][color] = true;
qNext.push([j, color as Color]);
}
}
}
q = qNext;
d++;
}
return ans;
}
type Graph = Record<number, number[]>;
type Color = 0 | 1;
type Vertex = [number, Color];
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
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.