[Week 4] LeetCode 743. Network Delay Time
LeetCode 743. Network Delay Time
問題描述:
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
-1
.
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 have1 <= u, v <= N
and1 <= w <= 100
.
題解:
很容易看出這是一個單源最短路徑問題,只需要求出從原點K
到圖中其它點的最短路徑,然後找到其中最長的即為題解,當然結果出現infinite
意味著該圖不連通,返回-1
即可。
這類問題的求解,我們需要知道一個資訊:是否會有負權邊出現?OK,這道題邊權代表時間,所以不會出現。對於無負邊權的單源最短路徑問題,我們很容易想到Dijkstra
演算法,那什麼是Dijkstra
演算法呢?
Dijkstra 演算法:
給定一個有向圖G(V,E)
,我們一開始初始化一個dist
陣列(dist[i]
指源點A
到頂點i
的距離),除了源點為0,其它頂點設定為infinite
。
- 我們從源點
A
開始,找到與源點相連的頂點,通過遍歷以A
dist
陣列,即設定源點到其直接相連點的距離。 - 然後找到
dist
數組裡除 源點A 外dist[i]
最小的頂點:C
,通過遍歷以C
為起點的邊,更新dist
陣列。 - 接著找到
dist 數組裡除 源點A 和 C 外
dist[i]最小的頂點:
B,通過遍歷以
B為起點的邊,更新
dist`陣列。 - 後面
D
和E
交給讀者自己分析,看看最後的結果:
# 虛擬碼
Input:G(V, E) -- a directed graph with vertices collection V and edges collection E
Output: For all vertices u reachable from source s, dist(u) is the distance from s to u
for all u in V:
dist(u) = infinite
dist(s) = 0
# 使用優先佇列找到值最小的點
Q = makequeue(V) (use dist as keys)
while Q is not empty:
u = Q.pop()
for all e(u,v) in E:
if dist(v) > dist(u) + l(u,v):
dist(v) = dist(u) + l(u,v)
decreaseKey(Q,v) # 讓佇列中元素重新排列
程式碼:
#include <vector>
#include <queue>
#include <climits>
using namespace std;
typedef pair<int, int> node;
int networkDelayTime(vector<vector<int> > ×, int N, int K)
{
/*
* initialize the graph :
* nodes are labelled 1 to N, so we let index start from 1 not 0
*/
vector<vector<int> > graph(N + 1, vector<int>(N + 1, -1));
for (int i = 0; i < times.size(); ++i)
{
vector<int> &e = times[i];
graph[e[0]][e[1]] = e[2];
}
/*
* dist[i] means distance of (K, i), INT_MAX means disconnected
* visited[i] means node has been closed or not
*/
vector<int> dist(N + 1, INT_MAX);
vector<bool> visited(N + 1, false);
dist[K] = 0;
priority_queue<node, vector<node>, greater<node> > que;
que.push(node(dist[K], K));
while (!que.empty())
{
node u = que.top();
que.pop();
int u_num = u.second;
if (visited[u_num])
continue;
for (int i = 1; i < graph[u_num].size(); ++i)
{
if (graph[u_num][i] != -1 && dist[i] > dist[u_num] + graph[u_num][i])
{
dist[i] = dist[u_num] + graph[u_num][i];
if (!visited[i])
que.push(node(dist[i], i));
}
}
}
int result = 0;
for (int i = 1; i <= N; ++i)
{
if (dist[i] == INT_MAX)
return -1;
else
result = max(result, dist[i]);
}
return result;
}
複雜度分析:
首先考慮一下不使用優先佇列的情況,每次我們都需要找到dist
陣列中值最小的點,複雜度為O(|V|)
;然後遍歷以該點為起點的邊,所以演算法結束會遍歷所有的邊,即O(|E|)
;總共需要遍歷|V|
次,所以總時間複雜度為 O(|V|^2)
。
使用優先佇列的情況,考慮應用二分堆實現的優先佇列。取出dist
中最小值點,因為刪除節點都會導致堆的結構調整,複雜度為O(log|V|)
;遍歷每條邊時,因為dist[i]
值的變化,需要調整堆的結構,複雜度為O(log|V|)
;總的來說,大概需要|V|
次取最小值點,|E|
次改變 dist[i]
的值,所以總複雜度為 O((|V|+|E|)log(|V|))