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:
-
Nwill be in the range[1, 100]. -
Kwill be in the range[1, N]. - The length of
timeswill be in the range[1, 6000]. - All edges
times[i] = (u, v, w)will have1 <= u, v <= Nand1 <= 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|))