LeetCode-743. 网络延迟时间
题目来源
题目详情
有 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 + 邻接矩阵
- 本题很容易想到是一道最短路问题的题目,因为它需要求解一个节点到另一个节点的最短路。但是,本题乍一看还不是说要求解最短路径,而是“最长路径”。它这里的最长路径指的是,所有最短路中的最长路径。所以,又回到了求解起点到每个节点之间的最短距离的问题。
- 那么,如何求解两个点之间的最短路呢?我们很容易就会想到dijkstra算法,因为这是最经典的用于求解一点到其他点之间最短路的算法。
- 说到底,dijkstra算法的原理就是,每次都找出一个没有访问的但是到起点距离最短的一个节点,再用这个节点去查看是否可以通过这个节点缩短起点到其他节点的距离,不断去更新这个距离。
- 需要注意的是,这里需要使用一个boolean数组来记录一个结点是否已经遍历过了,已经遍历过意味着从起点到这个节点的最短路已经确定了,无法通过任何其他节点作为中界来缩短距离。
class Solution {
public int networkDelayTime(int[][] times, int n, int k) {
// Dijkstra算法:从剩余顶点选择最短路径值最小的顶点。修改剩余顶点的最短路径值。
int maxs = 0x3f3f3f3f;
int[][] dis = new int[n][n];// 距离邻接矩阵
int[] mindis = new int[n];// mindis[i]表示i到起点的最短距离
boolean[] flag = new boolean[n];// 表示当前结点是否被访问过
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(i != j){
dis[i][j] = maxs;
}else{
dis[i][j] = 0;
}
}
mindis[i] = maxs;
}
mindis[k-1] = 0;// 起始结点到自己的最短距离为0
for(int[] edges : times){
int from = edges[0] - 1;
int to = edges[1] - 1;
int d = edges[2];
dis[from][to] = d;
}
for(int i=0; i<n; i++){
int minv = i;// 距离i最近的顶点
int mind = maxs;
for(int j=0; j<n; j++){
if(!flag[j] && mindis[j] <= mind){
mind = mindis[j];
minv = j;
}
}
flag[minv] = true;
for(int j=0; j<n; j++){
mindis[j] = Math.min(mindis[j], mindis[minv] + dis[minv][j]);
}
}
int ans = 0;
for(int i=0; i<n; i++){
if(mindis[i] == maxs){
return -1;
}
ans = Math.max(ans, mindis[i]);
}
return ans;
}
}
解法二:Dijkstra + 优先队列
- Dijkstra算法的另一种写法是使用优先队列去维护最短路径的顶点。因为从方法一我们可以看到,我们使用了一个外层循环,然后通过一个循环找到了当前未遍历过的距离起点最近的一个顶点,最后使用这个中间节点去更新其他节点的距离。
- 通过上述的描述,我们知道,可以借助优先队列去维护这个最小的距离顶点,然后,考虑到使用邻接矩阵会在顶点数太多是超出限制,所以我们这里使用邻接表的形式来存储每个顶点的所有边。
class Solution {
// 边类,每个顶点拥有的边
class Edge{
int to;// 边的终点
int cost;// 该边的距离
Edge(){}
Edge(int _to, int _cost){
this.to = _to;
this.cost = _cost;
}
}
// 起点到当前顶点的最短距离类
class MindisV{
int dis;// 起点到当前顶点的最短距离
int v;// 当前顶点
MindisV(){}
MindisV(int _dis, int _v){
this.dis = _dis;
this.v = _v;
}
}
public int networkDelayTime(int[][] times, int n, int k) {
// Dijkstra算法:从剩余顶点选择最短路径值最小的顶点。修改剩余顶点的最短路径值。
int maxs = 0x3f3f3f3f;
int[] mindis = new int[n];// mindis[i]表示i到起点的最短距离
for(int i=0; i<n; i++){
mindis[i] = maxs;
}
mindis[k-1] = 0;
List<Edge>[] edges = new ArrayList[n];
for(int i=0; i<n; i++){
edges[i] = new ArrayList<>();
}
for(int[] edge : times){
int from = edge[0] - 1;
int to = edge[1] - 1;
int d = edge[2];
edges[from].add(new Edge(to, d));
}
PriorityQueue<MindisV> que = new PriorityQueue<>((a, b) -> {
return a.dis - b.dis;// 根据距离由小到大排序,每次取出最小的
});
// 将起点自己放入队列中
que.offer(new MindisV(0, k-1));
while(!que.isEmpty()){
MindisV mindisv = que.poll();
int v = mindisv.v;
int dis = mindisv.dis;
// 表示当前节点的最短路径已经被更新了,这里就不要更新了
if(mindis[v] < dis){
continue;
}
for(int i=0; i<edges[v].size(); i++){
Edge e = edges[v].get(i);
if(mindis[e.to] > mindis[v] + e.cost){
mindis[e.to] = mindis[v] + e.cost;
que.offer(new MindisV(mindis[e.to], e.to));
}
}
}
int ans = 0;
for(int i=0; i<n; i++){
if(mindis[i] == maxs){
return -1;
}
ans = Math.max(ans, mindis[i]);
}
return ans;
}
}
Either Excellent or Rusty