题目描述
(中等)在一个有向图中,节点分别标记为 0, 1, …, n-1。图中每条边为红色或者蓝色,且存在自环或平行边。
red_edges 中的每一个 [i, j] 对表示从节点 i 到节点 j 的红色有向边。类似地,blue_edges 中的每一个 [i, j] 对表示从节点 i 到节点 j 的蓝色有向边。
返回长度为 n 的数组 answer,其中 answer[X] 是从节点 0 到节点 X 的红色边和蓝色边交替出现的最短路径的长度。如果不存在这样的路径,那么 answer[x] = -1。
示例:
输入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]]
输出:[0,1,1]
解题思路
单源最短路径问题,第一想法就是广度优先与Dijkstra算法。考虑到无权图,选择实现起来相对容易的广度优先算法。
相较于经典的最短路径问题,本题的难点在于如何高效的判断结点的重复访问(下文以去重代称)。传统的BFS去重可以借助dis[]数组(例如,初始化为-1,不为-1时表示该结点已找到最短路)或visited[]数组。但本题考虑到颜色交替这一特性,可能存在以不同颜色“重复” 访问某一结点后,可以到达原本无法到达的“目标”结点。
因此,只需在维护访问记录时增加color属性,即红/蓝色的访问分开维护。红色记为 ‘0’,蓝色记为 ‘1’。 介绍完整体思路后,代码细节如下。
代码实现
class Solution {
public:
vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& redEdges, vector<vector<int>>& blueEdges) {
//创建邻接链表,相当于索引,节省检索效率。
vector<vector<pair<int,int>>> graph(n);
for(auto edge : redEdges){
graph[edge[0]].push_back({edge[1], 0});
}
for(auto edge : blueEdges){
graph[edge[0]].push_back({edge[1], 1});
}
//初始化最短距离
vector<int> dis(n, -1);
//visited数组,第二维:下标0为红,1为蓝
vector<vector<bool>> visited(n, vector<bool>(2));
//0结点作为起点,拥有红蓝所有权限
visited[0] = {true, true};
//初始化最短距离
int distance = 0;
//起点的最短距离为0
dis[0] = distance;
//创建队列,起点入队,开始遍历graph
queue<pair<int, int>> que;
//[弧头(有向边起点),颜色(0--红, 1 -- 蓝, 2 -- all)]
que.push({0, 2});
while(!que.empty()){
int size = que.size();
//新的”一层“,距离 + 1
distance++;
while(size--){
auto node = que.front();
que.pop();
//遍历以当前结点为起点的所有边
for(auto edge : graph[node.first]){
//满足颜色交替 && 尚未以次颜色访问结点,边的终点入队
if(node.second != edge.second && !visited[edge.first][edge.second]){
//维护访问记录visited[当前边的终点(弧尾)][颜色]
visited[edge.first][edge.second] = true;
que.push({edge.first, edge.second});
//dis数组尚未修改过,即为首次访问,亦表示最短到达!
if(dis[edge.first] == -1) dis[edge.first] = distance;
}
}
}
}
//直到队列为空返回答案!
return dis;
}
};
运行结果: