Question
There are N network nodes, labelled 1 to N.
Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, and w is the time it takes for a signal to travel from source to target.
Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1.
Example 1:
Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2
Output: 2
Note:
- N will be in the range [1, 100].
- K will be in the range [1, N].
- The length of times will be in the range [1, 6000].
- All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 0 <= w <= 100.
Algorithm
很顯然,這一題用Dijkstra法解決,這個算法請參考《算法圖解》第七章。這一題,我們要思考兩個問題:
- 需要用到哪些數據結構?
- 如何用這些數據結構來實現Dijkstra算法?
需要用到哪些數據結構?
我們先來看看Dijkstra算法的步驟:(稍後來具體解釋)
- 找到可在最短時間內到達的節點
- 更新該節點鄰居的路徑長度(記錄到達每個節點路徑長度的列表)
- 重複,直到每個節點都執行過1和2(hash表記錄已經執行過的節點)
- 計算最短路徑
由上面的分析,可以得出需要一個數組和一個hash表(可以用其他方式替代),加上表示圖鄰接表,我們一共需要三張表
- 鄰接表(
vector<vector<pair< int, int>>> graph
)- 這裏用pair的形式是爲了方便coding
- 表示路徑長度(
vector<int> dis
) - 記錄已經執行過的節點(
unordered_set<int> hash
)
如何用這些數據結構來實現Dijkstra算法?
這裏用一個簡單的例子和圖來說明吧,遍歷完所有的節點,然後找到路徑長度表中最長的路徑長度即爲題目中的網絡延遲時間了。
Code
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<vector<pair<int, int>> > graph(N+1);
vector<int> dis(N+1, INT_MAX);
unordered_set<int> hash;
//create graph and init dis
for (auto edge : times){
graph[edge[0]].push_back(make_pair(edge[1], edge[2]));
}
dis[K] = 0;
int cur = K;
while (cur > 0){
hash.insert(cur);
int cur_dis = dis[cur];
for (auto node : graph[cur]){
int node_dis = cur_dis + node.second;
if (node_dis < dis[node.first] && hash.find(node.first) == hash.end()){
dis[node.first] = node_dis;
}
}
//find the next node
int tmp = INT_MAX;
cur = -1;
for (int i=1; i<=N; i++){
if (dis[i]<tmp && hash.find(i)==hash.end()){
tmp = dis[i];
cur = i;
}
}
}
int maxRes = 0;
for (int i=1; i<=N; i++){
maxRes = max(maxRes, dis[i]);
}
return maxRes == INT_MAX? -1 : maxRes;
}
};
上面的解法,時間複雜度一般,空間複雜度更是感人,看看有什麼可以優化的地方。
找下一個節點時,每次都用一遍循環,這裏我們每次只想找最小,所以可以用最小堆來優化這個地方。
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<vector<pair<int, int>> > graph(N+1);
vector<int> dis(N+1, INT_MAX);
unordered_set<int> hash;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> node_heap;
//create graph and init dis
for (auto edge : times){
graph[edge[0]].push_back(make_pair(edge[1], edge[2]));
}
dis[K] = 0;
node_heap.emplace(0, K);
while (!node_heap.empty()){
int cur = node_heap.top().second;
node_heap.pop();
hash.insert(cur);
int cur_dis = dis[cur];
for (auto node : graph[cur]){
int node_dis = cur_dis + node.second;
if (node_dis < dis[node.first] && hash.find(node.first) == hash.end()){
dis[node.first] = node_dis;
node_heap.emplace(node_dis, node.first);
}
}
//find the next node
int tmp = INT_MAX;
cur = -1;
for (int i=1; i<=N; i++){
if (dis[i]<tmp && hash.find(i)==hash.end()){
tmp = dis[i];
cur = i;
}
}
}
int maxRes = 0;
for (int i=1; i<=N; i++){
maxRes = max(maxRes, dis[i]);
}
return maxRes == INT_MAX? -1 : maxRes;
}
};