Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1129. Shortest Path with Alternating Colors - Leetcode Solution

Code Implementation

from collections import deque, defaultdict

def shortestAlternatingPaths(n, redEdges, blueEdges):
    graph = [defaultdict(list) for _ in range(2)]  # 0: red, 1: blue
    for u, v in redEdges:
        graph[0][u].append(v)
    for u, v in blueEdges:
        graph[1][u].append(v)

    res = [-1] * n
    visited = [[False] * n for _ in range(2)]  # visited[color][node]
    queue = deque()
    queue.append((0, 0, -1))  # (node, steps, prev_color), -1 means start

    while queue:
        node, steps, prev_color = queue.popleft()
        if res[node] == -1:
            res[node] = steps
        # Try both colors, but must alternate
        for color in [0, 1]:
            if color != prev_color:
                for nei in graph[color][node]:
                    if not visited[color][nei]:
                        visited[color][nei] = True
                        queue.append((nei, steps + 1, color))
    return res
      
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;

vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& redEdges, vector<vector<int>>& blueEdges) {
    vector<unordered_map<int, vector<int>>> graph(2); // 0: red, 1: blue
    for (auto& e : redEdges) graph[0][e[0]].push_back(e[1]);
    for (auto& e : blueEdges) graph[1][e[0]].push_back(e[1]);

    vector<int> res(n, -1);
    vector<vector<bool>> visited(2, vector<bool>(n, false));
    queue<tuple<int, int, int>> q; // node, steps, prev_color
    q.push({0, 0, -1});

    while (!q.empty()) {
        auto [node, steps, prev_color] = q.front(); q.pop();
        if (res[node] == -1) res[node] = steps;
        for (int color = 0; color < 2; ++color) {
            if (color != prev_color) {
                for (int nei : graph[color][node]) {
                    if (!visited[color][nei]) {
                        visited[color][nei] = true;
                        q.push({nei, steps + 1, color});
                    }
                }
            }
        }
    }
    return res;
}
      
import java.util.*;

public class Solution {
    public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) {
        Map<Integer, List<Integer>>[] graph = new HashMap[2];
        for (int i = 0; i < 2; ++i) graph[i] = new HashMap<>();
        for (int[] e : redEdges) graph[0].computeIfAbsent(e[0], k -> new ArrayList<>()).add(e[1]);
        for (int[] e : blueEdges) graph[1].computeIfAbsent(e[0], k -> new ArrayList<>()).add(e[1]);

        int[] res = new int[n];
        Arrays.fill(res, -1);
        boolean[][] visited = new boolean[2][n];
        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[]{0, 0, -1}); // node, steps, prev_color

        while (!q.isEmpty()) {
            int[] curr = q.poll();
            int node = curr[0], steps = curr[1], prevColor = curr[2];
            if (res[node] == -1) res[node] = steps;
            for (int color = 0; color < 2; ++color) {
                if (color != prevColor) {
                    List<Integer> neighbors = graph[color].getOrDefault(node, new ArrayList<>());
                    for (int nei : neighbors) {
                        if (!visited[color][nei]) {
                            visited[color][nei] = true;
                            q.offer(new int[]{nei, steps + 1, color});
                        }
                    }
                }
            }
        }
        return res;
    }
}
      
var shortestAlternatingPaths = function(n, redEdges, blueEdges) {
    const graph = [new Map(), new Map()]; // 0: red, 1: blue
    for (const [u, v] of redEdges) {
        if (!graph[0].has(u)) graph[0].set(u, []);
        graph[0].get(u).push(v);
    }
    for (const [u, v] of blueEdges) {
        if (!graph[1].has(u)) graph[1].set(u, []);
        graph[1].get(u).push(v);
    }

    const res = Array(n).fill(-1);
    const visited = [Array(n).fill(false), Array(n).fill(false)];
    const queue = [];
    queue.push([0, 0, -1]); // node, steps, prev_color

    while (queue.length) {
        const [node, steps, prevColor] = queue.shift();
        if (res[node] === -1) res[node] = steps;
        for (let color = 0; color < 2; ++color) {
            if (color !== prevColor) {
                const neighbors = graph[color].get(node) || [];
                for (const nei of neighbors) {
                    if (!visited[color][nei]) {
                        visited[color][nei] = true;
                        queue.push([nei, steps + 1, color]);
                    }
                }
            }
        }
    }
    return res;
};
      

Problem Description

You are given a directed graph with n nodes, labeled from 0 to n - 1. Some of the edges are colored red, and some are colored blue. You are given two lists: redEdges and blueEdges, where each element is a pair [from, to] representing a directed edge from from to to of the respective color.

Your goal is to find the shortest path from node 0 to every other node such that the edges taken alternate in color (no two consecutive edges can be the same color). If a node is unreachable under these constraints, return -1 for that node.

  • There may be multiple edges and self-loops.
  • The shortest path must alternate colors at every step.
  • Return a list of length n where result[i] is the length of the shortest alternating path from 0 to i, or -1 if not possible.

Thought Process

This problem is a twist on the classic shortest path problem in graphs, but with the added constraint that the path must alternate between red and blue edges. A brute force approach would consider all possible paths, but that quickly becomes infeasible as the number of paths grows exponentially with the number of nodes and edges.

The key insight is to use Breadth-First Search (BFS), which naturally finds the shortest path in unweighted graphs. However, we need to track the color of the last edge used to ensure the "alternating" requirement is satisfied. This means that visiting the same node via a red edge and a blue edge are considered different "states".

To avoid revisiting the same state, we must track visits not just by node, but also by the color of the edge used to reach it.

Solution Approach

We use a BFS approach, but with some modifications to handle alternating colors:

  1. Graph Representation: Build two adjacency lists/maps: one for red edges and one for blue edges. This allows quick lookup of neighbors by color.
  2. BFS Queue State: Each queue entry should store:
    • The current node
    • The current path length (steps so far)
    • The color of the edge used to arrive at this node (-1 for the starting node)
  3. Visited Tracking: Use a 2D visited array or map: visited[color][node]. This ensures we don't revisit the same node via the same color, which would create cycles or redundant searches.
  4. BFS Expansion: At each step, for the current node, try all outgoing edges of the opposite color from the previous edge. If the previous edge was red, try blue, and vice versa.
  5. Result Population: For each node, as soon as it is reached for the first time, record the path length. If a node is never reached, its value stays as -1.

This approach ensures that the shortest alternating path to each node is found efficiently, without redundant exploration.

Example Walkthrough

Sample Input:
n = 3
redEdges = [[0,1],[1,2]]
blueEdges = []

Step-by-step:

  1. Start at node 0, with no previous color.
  2. From node 0, can only take a red edge (since blue edges are empty). Move to node 1 via red, steps = 1.
  3. From node 1, we must now take a blue edge (to alternate), but there are none. So, we cannot proceed to node 2 under the alternating rule.
  4. Final distances: [0, 1, -1] — node 2 is unreachable with alternating colors.

If there were both red and blue edges, the BFS would explore both, always alternating, and update the shortest path for each node as soon as it is reached.

Time and Space Complexity

  • Brute Force: Exploring all possible paths could take exponential time in the number of nodes and edges, since paths can be arbitrarily long and cycles may occur.
  • BFS Approach:
    • Time Complexity: O(n + e), where n is the number of nodes and e is the total number of edges. Each node is visited at most twice (once per color), and all edges are explored once per color.
    • Space Complexity: O(n + e) for the adjacency lists, visited arrays, and BFS queue.

Summary

The problem is a variation of the shortest path search, with the twist that edge colors must alternate. By using BFS and keeping track of both the node and the color of the last edge used, we efficiently find the shortest alternating paths. The solution elegantly combines graph traversal with careful state management, ensuring we respect the color alternation constraint and avoid redundant exploration.