LeetCode-743. 網路延遲時間
阿新 • • 發佈:2022-03-31
題目來源
題目詳情
有 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
輸出:
示例 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;
}
}