[LeetCode] 743. Network Delay Time 网络延迟时间
There are N
network nodes, labelled 1
to N
.
Given times
, a list of travel times as directededges 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 have1 <= u, v <= N
and0 <= w <= 100
.
这道题给了我们一些有向边,又给了一个结点K,问至少需要多少时间才能从K到达任何一个结点。这实际上是一个有向图求最短路径的问题,求出K点到每一个点到最短路径,然后取其中最大的一个就是需要的时间了。可以想成从结点K开始有水流向周围扩散,当水流到达最远的一个结点时,那么其他所有的结点一定已经流过水了。最短路径的常用解法有迪杰斯特拉算法 Dijkstra Algorithm, 弗洛伊德算法 Floyd-Warshall Algorithm, 和贝尔曼福特算法 Bellman-Ford Algorithm,其中,Floyd 算法是多源最短路径,即求任意点到任意点到最短路径,而 Dijkstra 算法和 Bellman-Ford 算法是单源最短路径,即单个点到任意点到最短路径。这里因为起点只有一个K,所以使用单源最短路径就行了。这三种算法还有一点不同,就是 Dijkstra 算法处理有向权重图时,权重必须为正,而另外两种可以处理负权重有向图,但是不能出现负环,所谓负环,就是权重均为负的环。为啥呢,这里要先引入松弛操作 Relaxtion,这是这三个算法的核心思想,当有对边 (u, v) 是结点u到结点v,如果 dist(v) > dist(u) + w(u, v),那么 dist(v) 就可以被更新,这是所有这些的算法的核心操作。Dijkstra 算法是以起点为中心,向外层层扩展,直到扩展到终点为止。根据这特性,用 BFS 来实现时再好不过了,注意 while 循环里的第一层 for 循环,这保证了每一层的结点先被处理完,才会进入进入下一层,这种特性在用 BFS 遍历迷宫统计步数的时候很重要。对于每一个结点,都跟其周围的结点进行 Relaxtion 操作,从而更新周围结点的距离值。为了防止重复比较,需要使用 visited 数组来记录已访问过的结点,最后在所有的最小路径中选最大的返回,注意,如果结果 res 为 INT_MAX,说明有些结点是无法到达的,返回 -1。普通的实现方法的时间复杂度为 O(V2),基于优先队列的实现方法的时间复杂度为 O(E + VlogV),其中V和E分别为结点和边的个数,这里多说一句,Dijkstra 算法这种类贪心算法的机制,使得其无法处理有负权重的最短距离,还好这道题的权重都是正数,参见代码如下:
解法一:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int res = ;
vector<vector<int>> edges(, vector<int>(, -));
queue<int> q{{K}};
vector<int> dist(N + , INT_MAX);
dist[K] = ;
for (auto e : times) edges[e[]][e[]] = e[];
while (!q.empty()) {
unordered_set<int> visited;
for (int i = q.size(); i > ; --i) {
int u = q.front(); q.pop();
for (int v = ; v <= ; ++v) {
if (edges[u][v] != - && dist[u] + edges[u][v] < dist[v]) {
if (!visited.count(v)) {
visited.insert(v);
q.push(v);
}
dist[v] = dist[u] + edges[u][v];
}
}
}
}
for (int i = ; i <= N; ++i) {
res = max(res, dist[i]);
}
return res == INT_MAX ? - : res;
}
};
下面来看基于 Bellman-Ford 算法的解法,时间复杂度是 O(VE),V和E分别是结点和边的个数。这种算法是基于 DP 来求全局最优解,原理是对图进行 V - 1 次松弛操作,这里的V是所有结点的个数(为啥是 V-1 次呢,因为最短路径最多只有 V-1 条边,所以只需循环 V-1 次),在重复计算中,使得每个结点的距离被不停的更新,直到获得最小的距离,这种设计方法融合了暴力搜索之美,写法简洁又不失优雅。之前提到了,Bellman-Ford 算法可以处理负权重的情况,但是不能有负环存在,一般形式的写法中最后一部分是检测负环的,如果存在负环则报错。不能有负环原因是,每转一圈,权重和都在减小,可以无限转,那么最后的最小距离都是负无穷,无意义了。没有负环的话,V-1 次循环后各点的最小距离应该已经收敛了,所以在检测负环时,就再循环一次,如果最小距离还能更新的话,就说明存在负环。这道题由于不存在负权重,所以就不检测了,参见代码如下:
解法二:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int res = ;
vector<int> dist(N + , INT_MAX);
dist[K] = ;
for (int i = ; i < N; ++i) {
for (auto e : times) {
int u = e[], v = e[], w = e[];
if (dist[u] != INT_MAX && dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
}
}
}
for (int i = ; i <= N; ++i) {
res = max(res, dist[i]);
}
return res == INT_MAX ? - : res;
}
};
下面这种解法是 Bellman Ford 解法的优化版本,由热心网友旅叶提供。之所以能提高运行速度,是因为使用了队列 queue,这样对于每个结点,不用都松弛所有的边,因为大多数的松弛计算都是无用功。优化的方法是,若某个点的 dist 值不变,不去更新它,只有当某个点的 dist 值被更新了,才将其加入 queue,并去更新跟其相连的点,同时还需要加入 HashSet,以免被反复错误更新,这样的时间复杂度可以优化到 O(E+V)。Java 版的代码在评论区三楼,旅叶声称可以 beat 百分之九十多,但博主改写的这个 C++ 版本的却只能 beat 百分之二十多,hmm,因缺斯汀。不过还是要比上面的解法二快很多,博主又仔细看了看,发现很像解法一和解法二的混合版本哈,参见代码如下:
解法三:
class Solution {
public:
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
int res = ;
unordered_map<int, vector<pair<int, int>>> edges;
vector<int> dist(N + , INT_MAX);
queue<int> q{{K}};
dist[K] = ;
for (auto e : times) edges[e[]].push_back({e[], e[]});
while (!q.empty()) {
int u = q.front(); q.pop();
unordered_set<int> visited;
for (auto e : edges[u]) {
int v = e.first, w = e.second;
if (dist[u] != INT_MAX && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
if (visited.count(v)) continue;
visited.insert(v);
q.push(v);
}
}
}
for (int i = ; i <= N; ++i) {
res = max(res, dist[i]);
}
return res == INT_MAX ? - : res;
}
};
讨论:最后再来说说这个 Floyd 算法,这也是一种经典的动态规划算法,目的是要找结点i到结点j的最短路径。而结点i到结点j的走法就两种可能,一种是直接从结点i到结点j,另一种是经过若干个结点k到达结点j。所以对于每个中间结点k,检查 dist(i, k) + dist(k, j) < dist(i, j) 是否成立,成立的话就松弛它,这样遍历完所有的结点k,dist(i, j) 中就是结点i到结点j的最短距离了。时间复杂度是 O(V3),处处透露着暴力美学。除了这三种算法外,还有一些很类似的优化算法,比如 Bellman-Ford 的优化算法- SPFA 算法,还有融合了 Bellman-Ford 和 Dijkstra 算法的高效的多源最短路径算法- Johnson 算法,这里就不过多赘述了,感兴趣的童鞋可尽情的 Google 之~
Github 同步地址:
https://github.com/grandyang/leetcode/issues/743
参考资料:
https://leetcode.com/problems/network-delay-time/description/
https://leetcode.com/problems/network-delay-time/discuss/109982/C++-Bellman-Ford
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] 743. Network Delay Time 网络延迟时间的更多相关文章
- [LeetCode] Network Delay Time 网络延迟时间
There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edges ti ...
- [LeetCode] Network Delay Time 网络延迟时间——最短路算法 Bellman-Ford(DP) 和 dijkstra(本质上就是BFS的迭代变种)
There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edges ti ...
- LeetCode 743. Network Delay Time
原题链接在这里:https://leetcode.com/problems/network-delay-time/ 题目: There are N network nodes, labelled 1 ...
- 【LeetCode】743. Network Delay Time 解题报告(Python)
[LeetCode]743. Network Delay Time 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: ht ...
- 743. Network Delay Time
题目来源: https://leetcode.com/problems/network-delay-time/ 自我感觉难度/真实难度: 题意: 分析: 自己的代码: class Solution: ...
- 【leetcode】Network Delay Time
题目: There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edge ...
- Java实现 LeetCode 743 网络延迟时间(Dijkstra经典例题)
743. 网络延迟时间 有 N 个网络节点,标记为 1 到 N. 给定一个列表 times,表示信号经过有向边的传递时间. times[i] = (u, v, w),其中 u 是源节点,v 是目标节点 ...
- NFS - Network File System网络文件系统
NFS(Network File System/网络文件系统): 设置Linux系统之间的文件共享(Linux与Windows中间文件共享采用SAMBA服务): NFS只是一种文件系统,本身没有传输功 ...
- 以Network Dataset(网络数据集)方式实现的最短路径分析
转自原文 以Network Dataset(网络数据集)方式实现的最短路径分析 构建网络有两种方式,分别是网络数据集NetworkDataset和几何网络Geometric Network,这个网络结 ...
随机推荐
- 绿联Type-C千兆网卡AX88179芯片驱动(苹果Mac OSX系统)CM141丨CM179
绿联Type-C千兆网卡AX88179芯片驱动(苹果Mac OSX系统)CM141丨CM179 下载地址:https://www.lulian.cn/download/6-cn.html AX8817 ...
- python xpath图片爬取
import requests from urllib.request import urlretrieve from lxml import etree headers = { 'User-Agen ...
- Python urlib 模块
Python urlib 模块 urlib 模块 当前再爬虫领域使用的比较少,不过它对图片爬取处理会比较方便.这里我们只使用它的图片爬取. 使用 urlib.request.urlretrieve(u ...
- Netty中的ChannelPipeline源码分析
ChannelPipeline在Netty中是用来处理请求的责任链,默认实现是DefaultChannelPipeline,其构造方法如下: private final Channel channel ...
- C# in 参数修饰符
in 修饰符记录: 新版C# 新增加的 in 修饰符:保证发送到方法当中的数据不被更改(值类型),当in 修饰符用于引用类型时,可以改变变量的内容,单不能更改变量本身. 个人理解:in 修饰符传递的数 ...
- 配置IIS网站可以下载.apk/.ipa文件
添加 扩展名是:.apk MIMI类型是:application/vnd.android.package-archive 扩展名是:.ipa MIMI类型是:application/iphone
- badboy录制过程中出现当前页面的脚本发现错误
为什么出现这个提示 , 是因为访问者使用的浏览器不能完全支持页面里的脚本,毕竟版本太老,一直没有更新 ,这个版本错误并不会影响使用,有强迫症的可以关闭下,
- 50本.NTE、C#相关技术书籍免费下载
场景 近期囤积了一大批编程教程和电子书资料.至于视频教程,我一般是看完之后整理成相应的博客进行记录,一般不会再云盘中进行存取,因为很占空间. 至于电子书资料,很多,就是得一点点整理归纳. 近期我的公众 ...
- SQLi-LABS Page-1(Basic Challenges) Less1-Less4
Less-1 GET - Error based - Single quotes - Stri http://10.10.202.112/sqli/Less-1?id=1 http://10.10.2 ...
- swift开发之--简单封装Alamofire请求类以及简单使用SnapKit
以前在swift3的时候,写过类似的,那个时候还没有很成熟的网络请求类库,在这里,还是衷心感谢大神们的付出! 具体效果如下,先上图: 点击按钮的时候,请求数据,数据结构如下: { ; reason = ...