题目概述
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 have 1 <= u, v <= N and 1 <= w <= 100.
分析
此题给定我们一张图中若干节点的连通性信息以及节点间的距离,之后要求我们判断针对某个特定节点(第K个节点),其能否和其他所有结点连通,并求出要到达所有节点需要经过的网络时延。
经过分析,可以明确两个子问题,即:
- 判断两个节点之间是否连通
- 求出某个节点到图中其他任意可达节点所需的最短路径
对于前一个子问题,只需要读入题目给定参数的数据。可以使用一个二维数组预先设定所有节点之间的距离之间为不可达;之后根据times数组中的距离信息,不断更新图上节点之间的距离,最后直接根据下标判断两个节点之间的距离是否发生改变即可。
对于后一个子问题,可以使用Floyd-Warshall算法或Bellman-Ford算法进行处理。下面是两种方法分别解题的流程:
// Floyd-Warshall
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int max_Distance = 100*100; // 两个节点之间的最远距离
// 初始化所有节点之间的距离为最远距离
vector< vector<int> > graph(N, vector<int>(N, max_Distance));
// 处理图信息,将可以到达的点之间的路径距离存入表中
for (auto time : times) graph[time[0] - 1][time[1] - 1] = time[2];
for (int i = 0; i < N; i++) graph[i][i] = 0;
// Floyd-Warshall算法遍历图,更新点路径距离
for (int k = 0; k < N; k++)
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);
// 寻找特定K节点
int ans = INT_MIN;
for(int i = 0; i < N; i++) {
// 存在某个点达不到
if (graph[K-1][i] >= max_Distance) return -1;
// 时间取决于最远的路径
ans = max(ans, graph[K-1][i]);
}
return ans;
}
容易发现,使用Floyd-Warshall算法的时间复杂度为O(n3),空间复杂度为O(n2)
// Bellman-Ford
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int max_Distance = 100*100; // 两个节点之间的最远距离
// 初始化所有节点之间的距离为最远距离
vector<int> dist(N, max_Distance);
// 第K个点作为起点
dist[K-1] = 0;
for(int i = 1; i < N; i++)
for (auto time : times) {
// Bellman-Ford
int u = time[0] - 1, v = time[1] - 1, w = time[2];
dist[v] = min(dist[v], dist[u] + w);
}
// 寻找最远的路径
int D = *max_element(dist.begin(), dist.end());
return D == max_Distance ? -1 : D;
}
使用Bellman-Ford算法,时间复杂度为O(ne),其中n为图中节点数,e为图中边数;空间复杂度为O(n)。
总结
此题的精髓在于总结出了两种求解图中任意两点之间的最短距离的算法。
Floyd-Warshall算法的本质是一个动态规划的过程,即通过三层循环,不断地在两个点A、B之间寻找第三个点C,判断是否能满足AB之间的距离小于AC+CB的距离之和。这一过程需要不断地更新图中的节点距离信息,而且是直接应用于全图的所有节点,因此时间复杂度和空间复杂度相对来说较大。
Bellman-Ford算法本质同样是基于动态规划,不过相比Floyd-Warshall算法要精简一些,因为其预先指定了一个点作为起点,因此只需要存储其他节点到该起点的距离,空间复杂度上会小一些。但在判断最短距离的过程中,仍然需要参考全图,大致判断思路也与Floyd-Warshall算法类似,即判断起点到某个点A距离,是否大于源点到某个点B的距离 + AB的距离。该算法时间复杂度取决于图中节点数和边数,因此如果图相对稀疏,即边较少的情况下,使用Bellman-Ford算法将比较节省时间。