本文涉及知识点

C++BFS算法
C++图论

LeetCode743. 网络延迟时间

有 n 个网络节点,标记为 1 到 n。
给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。
现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。
示例 1:

在这里插入图片描述

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
输出:2
示例 2:

输入:times = [[1,2,1]], n = 2, k = 1
输出:1
示例 3:

输入:times = [[1,2,1]], n = 2, k = 2
输出:-1

提示:

1 <= k <= n <= 100
1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
所有 (ui, vi) 对都 互不相同(即,不含重复边)

稠密图单源最短路径

本题本质是求:单源最短路径。由于本题是稠密图故用朴素迪氏单源路径。
时间复杂度:O(nn)
由于本题解,主要讲解BFS,故只简述原理:
dis[x]记录x到k的最短距离,将dis按升序排序后变成dis1。有如下性质:
dis[i]的最短路径任何节点i1,dis[i1]一定小于等于dis[i]。即:我们按dis[i]从小到大处理,一定可以保证无后效性。
思路:
处理cur节点时:更新所有和cur直接相连节点的dis。注意:如果新dis大于旧dis,则不更新。
未处理节点中寻找,寻找dis 最小的的节点开始处理。如果不存在,即所有和源点连接的节点都已经处理完毕,则结束。

BFS

错误解法

∀ x i n \forall x \quad in xin leves[i] , x到k的最短路径经过i个点。dis[i] 记录i到k的最短距离。
利用数学归纳法来证明:
如果i等于0,leves[0] = {k}。
i>1,则i的最短路径的倒数第二个点x1:一定属于leves[i-1]
故按leves[i]的顺序BFS,可保证无后效性。
时间复杂度:O(nn)
错误原因:
在这里插入图片描述
经过一个节点到c的记录是3,经过两个节点到c的距离是2。
某节点x可能经过i+1个节点比经过i1更短,需要重新枚举。

正确解法

处理完leves[0],c属于leves[1],处理完leves[1],c属于leves[2]。极端情况:
n个节点加入n次leves,故空间复杂度:O(nn)
每个leves元素的后续节点处理时间复杂度:O(n)
故总时间复杂度:O(nnn),本题能过。
BFS的状态:leves[i]的任意元素x,从k到x存在经过i个节点的路径。
BFS的后续状态:next和cur相连,且dis[cur]+ dis(cur,next) <= dis[next]。
BFS的初始值:leves[0] = {k},dis[k]=0,其它dis为1e6。
BFS返回值:max(dis),如果为1e6,则返回-1。
BFS重复状态处理:leves可以有重复元素,leves[i]没重复元素。
注意:有向图 节点编号从1开始。

代码

核心代码

class Solution {
		public:
			int networkDelayTime(vector<vector<int>>& times, int n, int k) {
				vector < vector<pair<int, int>>> vNeiBo(n);
				for (const auto& v : times) {
					vNeiBo[v[0] - 1].emplace_back(v[1] - 1, v[2]);
				}
				const int iNotMay = 1e6;
				vector<int> dis(n, iNotMay);
				dis[k-1] = 0;
				vector<unordered_set<int>> leves = { {k-1} };
				for (int i = 0; i < leves.size(); i++) {
					unordered_set<int> nexts;
					for (const auto& cur : leves[i]) {
						for (const auto& [next, w] : vNeiBo[cur]) {
							const int iNew = dis[cur] + w;
							if (iNew < dis[next]) {
								dis[next] = iNew;
								nexts.emplace(next);
							}
						}
					}
					if (nexts.empty()) { break; }
					leves.emplace_back(nexts);
				}
				const int iMax = *std::max_element(dis.begin(), dis.end());
				return (iNotMay != iMax) ? iMax : -1;
			}
		};

单元测试

		TEST_METHOD(TestMethod1)
		{
			times = { {2,1,1},{2,3,1},{3,4,1} }, n = 4, k = 2;
			auto res = Solution().networkDelayTime(times, n, k);
			AssertEx(2, res);
		}
		TEST_METHOD(TestMethod2)
		{
			times = { {1,2,1} }, n = 2, k = 1;
			auto res = Solution().networkDelayTime(times, n, k);
			AssertEx(1, res);
		}
		TEST_METHOD(TestMethod3)
		{
			times = { {1,2,1} }, n = 2, k = 2;
			auto res = Solution().networkDelayTime(times, n, k);
			AssertEx(-1, res);
		}

扩展阅读

我想对大家说的话
亲士工具箱:支持AutoCad2013及以上
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
活到老,学到老。明朝中后期,大约50%的进士能当上堂官(副部及更高);能当上堂官的举人只有十余人。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐