Medium

题目描述

给你一个整数 n,表示一个有向图中节点的数目,节点编号为 0n - 1。图中的每条边都是红色或者蓝色的,且可能存在自环或平行边。

给你两个数组 redEdgesblueEdges,其中:

  • redEdges[i] = [ai, bi] 表示图中存在一条从节点 ai 到节点 bi 的红色有向边
  • blueEdges[j] = [uj, vj] 表示图中存在一条从节点 uj 到节点 vj 的蓝色有向边

返回长度为 n 的数组 answer,其中 answer[x] 是从节点 0 到节点 x 的最短路径的长度,且路径上的边的颜色必须交替出现。如果不存在这样的路径,则用 -1 作为答案。

示例 1:

输入:n = 3, redEdges = [[0,1],[1,2]], blueEdges = []
输出:[0,1,-1]

示例 2:

输入:n = 3, redEdges = [[0,1]], blueEdges = [[2,1]]
输出:[0,1,-1]

提示:

  • 1 <= n <= 100
  • 0 <= redEdges.length, blueEdges.length <= 400
  • redEdges[i].length == blueEdges[j].length == 2
  • 0 <= ai, bi, uj, vj < n

解题思路

这是一个带有约束条件的最短路径问题,约束是路径上的边颜色必须交替出现。

核心思路: 将问题转换为在扩展图上的最短路径问题。我们可以将每个节点分成两个状态:

  • (node, RED):到达该节点时最后一条边是红色
  • (node, BLUE):到达该节点时最后一条边是蓝色

算法步骤:

  1. 构建红色和蓝色的邻接表
  2. 使用BFS从起点0开始搜索,初始状态包含 (0, RED)(0, BLUE) 两种可能
  3. 对于每个状态 (node, color),只能通过与当前颜色不同的边进行扩展
  4. 使用访问数组避免重复访问同一状态
  5. 记录到达每个节点的最短距离

时间复杂度分析:

  • 每个节点最多访问2次(红色状态和蓝色状态各一次)
  • 总的状态数为 O(n),每条边最多被处理一次
  • 时间复杂度:O(n + redEdges.length + blueEdges.length)

这种方法确保了找到的路径满足颜色交替的约束,且是最短的。

代码实现

class Solution {
public:
    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& redEdges, vector<vector<int>>& blueEdges) {
        // 构建邻接表
        vector<vector<int>> redGraph(n), blueGraph(n);
        for (auto& edge : redEdges) {
            redGraph[edge[0]].push_back(edge[1]);
        }
        for (auto& edge : blueEdges) {
            blueGraph[edge[0]].push_back(edge[1]);
        }
        
        vector<int> result(n, -1);
        // visited[node][color] - color: 0=red, 1=blue
        vector<vector<bool>> visited(n, vector<bool>(2, false));
        
        // BFS: {node, color, distance}
        queue<tuple<int, int, int>> q;
        q.push({0, 0, 0}); // start with red
        q.push({0, 1, 0}); // start with blue
        visited[0][0] = visited[0][1] = true;
        result[0] = 0;
        
        while (!q.empty()) {
            auto [node, color, dist] = q.front();
            q.pop();
            
            // Choose next edges with opposite color
            vector<vector<int>>& graph = (color == 0) ? blueGraph : redGraph;
            int nextColor = 1 - color;
            
            for (int next : graph[node]) {
                if (!visited[next][nextColor]) {
                    visited[next][nextColor] = true;
                    if (result[next] == -1) {
                        result[next] = dist + 1;
                    }
                    q.push({next, nextColor, dist + 1});
                }
            }
        }
        
        return result;
    }
};
class Solution:
    def shortestAlternatingPaths(self, n: int, redEdges: List[List[int]], blueEdges: List[List[int]]) -> List[int]:
        from collections import defaultdict, deque
        
        # 构建邻接表
        red_graph = defaultdict(list)
        blue_graph = defaultdict(list)
        
        for a, b in redEdges:
            red_graph[a].append(b)
        for u, v in blueEdges:
            blue_graph[u].append(v)
        
        result = [-1] * n
        # visited[node][color] - color: 0=red, 1=blue
        visited = [[False, False] for _ in range(n)]
        
        # BFS: (node, color, distance)
        queue = deque([(0, 0, 0), (0, 1, 0)])  # start with both colors
        visited[0][0] = visited[0][1] = True
        result[0] = 0
        
        while queue:
            node, color, dist = queue.popleft()
            
            # Choose next edges with opposite color
            graph = blue_graph if color == 0 else red_graph
            next_color = 1 - color
            
            for next_node in graph[node]:
                if not visited[next_node][next_color]:
                    visited[next_node][next_color] = True
                    if result[next_node] == -1:
                        result[next_node] = dist + 1
                    queue.append((next_node, next_color, dist + 1))
        
        return result
public class Solution {
    public int[] ShortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) {
        // 构建邻接表
        List<int>[] redGraph = new List<int>[n];
        List<int>[] blueGraph = new List<int>[n];
        for (int i = 0; i < n; i++) {
            redGraph[i] = new List<int>();
            blueGraph[i] = new List<int>();
        }
        
        foreach (var edge in redEdges) {
            redGraph[edge[0]].Add(edge[1]);
        }
        foreach (var edge in blueEdges) {
            blueGraph[edge[0]].Add(edge[1]);
        }
        
        int[] result = new int[n];
        Array.Fill(result, -1);
        // visited[node, color] - color: 0=red, 1=blue
        bool[,] visited = new bool[n, 2];
        
        // BFS: (node, color, distance)
        Queue<(int node, int color, int dist)> queue = new Queue<(int, int, int)>();
        queue.Enqueue((0, 0, 0)); // start with red
        queue.Enqueue((0, 1, 0)); // start with blue
        visited[0, 0] = visited[0, 1] = true;
        result[0] = 0;
        
        while (queue.Count > 0) {
            var (node, color, dist) = queue.Dequeue();
            
            // Choose next edges with opposite color
            List<int>[] graph = (color == 0) ? blueGraph : redGraph;
            int nextColor = 1 - color;
            
            foreach (int next in graph[node]) {
                if (!visited[next, nextColor]) {
                    visited[next, nextColor] = true;
                    if (result[next] == -1) {
                        result[next] = dist + 1;
                    }
                    queue.Enqueue((next, nextColor, dist + 1));
                }
            }
        }
        
        return result;
    }
}
var shortestAlternatingPaths = function(n, redEdges, blueEdges) {
    const redGraph = Array(n).fill().map(() => []);
    const blueGraph = Array(n).fill().map(() => []);
    
    for (const [a, b] of redEdges) {
        redGraph[a].push(b);
    }
    for (const [u, v] of blueEdges) {
        blueGraph[u].push(v);
    }
    
    const result = Array(n).fill(-1);
    result[0] = 0;
    
    const queue = [[0, 0, 0], [0, 0, 1]]; // [node, distance, lastColor (0=red, 1=blue)]
    const visited = Array(n).fill().map(() => [false, false]);
    
    while (queue.length > 0) {
        const [node, dist, lastColor] = queue.shift();
        
        if (visited[node][lastColor]) continue;
        visited[node][lastColor] = true;
        
        if (result[node] === -1) {
            result[node] = dist;
        }
        
        if (lastColor === 0) {
            for (const neighbor of blueGraph[node]) {
                if (!visited[neighbor][1]) {
                    queue.push([neighbor, dist + 1, 1]);
                }
            }
        } else {
            for (const neighbor of redGraph[node]) {
                if (!visited[neighbor][0]) {
                    queue.push([neighbor, dist + 1, 0]);
                }
            }
        }
    }
    
    return result;
};

复杂度分析

复杂度类型分析
时间复杂度O(n + E),其中 E 为边的总数。每个节点最多访问2次(红蓝状态各一次),每条边最多处理一次
空间复杂度O(n + E),用于存储邻接表、访问数组和BFS队列