难度​medium​

有 n 个网络节点,标记为 1 到 n。

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。

示例 1:

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2

输出:2

示例 2:

输入:times = [[1,2,1]], n = 2, k = 1

输出:1

示例 3:

输入:times = [[1,2,1]], n = 2, k = 2

输出:-1

提示:

1 <= k <= n <= 100

1 <= times.length <= 6000

times[i].length == 3

1 <= ui, vi <= n

ui != vi

0 <= wi <= 100

所有 (ui, vi) 对都 互不相同(即,不含重复边)

解题思路:这道题目是单源最短路径问题,主要有两种解决方法:Dijkstra算法和Bellman-ford算法。

Dijkstra算法采用的是BFS的思路,Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离,初始时,原点k的路径权重被赋为0(dis[k]=0)。若对于顶点k存在能直接到达的边(u, v),则把更新dis[v]的值,然后把当前访问过的点v加入一个队列中,这就是BFS往外遍历的下一步的候选点,这样每一轮都有一批候选点,对每个候选点都要考虑它所有可能的路径,然后更新路径终点的dis值,当队列中已经没有候选点的时候,说明BFS遍历已经结束,这时候dis中保存的就是从原点出发到各个顶点的最小距离,取其中最大值,返回即可。

代码

class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
int[][] edges = new int[101][101];
int[] dis = new int[n+1];
Arrays.fill(dis, Integer.MAX_VALUE);
for(int[] edge : edges) Arrays.fill(edge, -1);
Queue<Integer> q = new LinkedList<>();
q.add(k);
dis[k] = 0;
dis[0] = 0;
for(int[] time : times) edges[time[0]][time[1]] = time[2];
while(!q.isEmpty()){
int sz = q.size();
HashSet<Integer> visited = new HashSet<>();
for(int m=sz; m>0; m--){
int u = q.poll();
for(int v=0; v<=100; v++){
if(edges[u][v]!=-1 && dis[u]+edges[u][v]<dis[v]){
if(!visited.contains(v)){
visited.add(v);
q.add(v);
}
dis[v] = dis[u] + edges[u][v];
}
}
}
}
int res = 0;
for(int i=0; i<dis.length; i++) res = Math.max(res, dis[i]);
return res == Integer.MAX_VALUE ? -1 : res;
}
}

解题思路:Bellman-ford算法接近暴力解法,因为这个有向图里面,假设有v个节点,最长的路径也只能是v-1,因此用v-1轮遍历,每次遍历,都对所有的边进行松弛操作,即对于一个路径对[u,v,w]如果dis[v]>dis[u]+w,则更新dis[v]。

其主要思想:对所有的边进行n-1轮松弛操作,因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边。换句话说,第1轮在对所有的边进行松弛后,得到的是源点最多经过一条边到达其他顶点的最短距离;第2轮在对所有的边进行松弛后,得到的是源点最多经过两条边到达其他顶点的最短距离;第3轮在对所有的边进行松弛后,得到的是源点最多经过三条边到达其他顶点的最短距离......

我们先看 Bellman-ford的核心代码

for (var i = 0; i < n - 1; i++) {
for (var j = 0; j < m; j++) {//对m条边进行循环
var edge = edges[j];
// 松弛操作
if (distance[edge.to] > distance[edge.from] + edge.weight ){
distance[edge.to] = distance[edge.from] + edge.weight;
}
}
}

其中, n为顶点的个数,m为边的个数,edges数组储存了所有边,distance数组是源点到所有点的距离估计值,循环结束后就是最小值。

代码

class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
int[] dis = new int[n+1];
Arrays.fill(dis, Integer.MAX_VALUE);
dis[k] = 0;
for(int i=1; i<n; i++){
for(int[] time : times){
int u = time[0], v = time[1], w = time[2];
if(dis[u]!=Integer.MAX_VALUE && dis[v]>dis[u] + w){
dis[v] = dis[u] + w;
}
}
}
int res = 0;
for(int i=1; i<dis.length; i++) res = Math.max(res, dis[i]);
return res== Integer.MAX_VALUE ? -1 : res;
}
}