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
. How long will it take for all nodes to receive the signal? If it is impossible, return -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|))