【C++BFS】743. 网络延迟时间

本文涉及知识点

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);
		}


如果有不明白的,请加文末QQ群。

扩展阅读

视频课程

先学简单的课程,请移步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++**实现。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,以下是一个基于C++BFS求最短路径的示例代码: ```cpp #include <iostream> #include <queue> #include <cstring> using namespace std; const int MAXN = 1005; // 最大节点数 const int INF = 0x3f3f3f3f; // 无穷大 int n, m; // n表示节点个数,m表示边数 int dis[MAXN]; // 存储起点到每个节点的距离 bool vis[MAXN]; // 记录每个节点是否被访问过 int head[MAXN], to[MAXN<<1], nxt[MAXN<<1], edge[MAXN<<1], cnt; // 邻接表存图 void addEdge(int u, int v, int w) { to[++cnt] = v; nxt[cnt] = head[u]; edge[cnt] = w; head[u] = cnt; } void bfs(int start) { memset(dis, INF, sizeof(dis)); // 初始化距离为无穷大 memset(vis, false, sizeof(vis)); // 初始化所有节点都未被访问过 dis[start] = 0; // 起点到自己的距离为0 vis[start] = true; // 起点已经访问过了 queue<int> q; // 定义一个队列,用于广度优先搜索 q.push(start); // 将起点加入队列 while (!q.empty()) { int u = q.front(); q.pop(); // 取出队首节点 for (int i = head[u]; i; i = nxt[i]) { int v = to[i], w = edge[i]; if (!vis[v]) { // 如果这个节点未被访问过 dis[v] = dis[u] + w; // 更新距离 vis[v] = true; // 标记为已访问 q.push(v); // 将其加入队列 } } } } int main() { cin >> n >> m; for (int i = 1; i <= m; ++i) { int u, v, w; cin >> u >> v >> w; addEdge(u, v, w); addEdge(v, u, w); // 无向图需要加两条边 } int start; cin >> start; bfs(start); for (int i = 1; i <= n; ++i) { if (dis[i] != INF) { cout << start << " 到 " << i << " 的最短距离为:" << dis[i] << endl; } } return 0; } ``` 这段代码使用邻接表存图,时间复杂度为O(n+m),其中n表示节点数,m表示边数。在实际使用中,可以根据自己的需求进行修改。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值