当前位置: 首页 > article >正文

【C++图论 最短路】2642. 设计可以求最短路径的图类|1810

本文涉及知识点

C++图论

LeetCode2642. 设计可以求最短路径的图类

给你一个有 n 个节点的 有向带权 图,节点编号为 0 到 n - 1 。图中的初始边用数组 edges 表示,其中 edges[i] = [fromi, toi, edgeCosti] 表示从 fromi 到 toi 有一条代价为 edgeCosti 的边。
请你实现一个 Graph 类:
Graph(int n, int[][] edges) 初始化图有 n 个节点,并输入初始边。
addEdge(int[] edge) 向边集中添加一条边,其中 edge = [from, to, edgeCost] 。数据保证添加这条边之前对应的两个节点之间没有有向边。
int shortestPath(int node1, int node2) 返回从节点 node1 到 node2 的路径 最小 代价。如果路径不存在,返回 -1 。一条路径的代价是路径中所有边代价之和。

示例 1:
在这里插入图片描述

输入:
[“Graph”, “shortestPath”, “shortestPath”, “addEdge”, “shortestPath”]
[[4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]], [3, 2], [0, 3], [[1, 3, 4]], [0, 3]]
输出:
[null, 6, -1, null, 6]

解释:
Graph g = new Graph(4, [[0, 2, 5], [0, 1, 2], [1, 2, 1], [3, 0, 3]]);
g.shortestPath(3, 2); // 返回 6 。从 3 到 2 的最短路径如第一幅图所示:3 -> 0 -> 1 -> 2 ,总代价为 3 + 2 + 1 = 6 。
g.shortestPath(0, 3); // 返回 -1 。没有从 0 到 3 的路径。
g.addEdge([1, 3, 4]); // 添加一条节点 1 到节点 3 的边,得到第二幅图。
g.shortestPath(0, 3); // 返回 6 。从 0 到 3 的最短路径为 0 -> 1 -> 3 ,总代价为 2 + 4 = 6 。

提示:
1 <= n <= 100
0 <= edges.length <= n * (n - 1)
edges[i].length == edge.length == 3
0 <= fromi, toi, from, to, node1, node2 <= n - 1
1 <= edgeCosti, edgeCost <= 106
图中任何时候都不会有重边和自环。
调用 addEdge 至多 100 次。
调用 shortestPath 至多 100 次。

稀疏图最短路

用迪氏堆优化最短路。
每次shortestPath的时间复杂度:O(mlogm)。m是边数。

代码

核心代码

class CNeiBo
{
public:	
	static vector<vector<int>> Two(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0) 
	{
		vector<vector<int>>  vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<std::pair<int, int>>> Three(int n, vector<vector<int>>& edges, bool bDirect, int iBase = 0)
	{
		vector<vector<std::pair<int, int>>> vNeiBo(n);
		for (const auto& v : edges)
		{
			vNeiBo[v[0] - iBase].emplace_back(v[1] - iBase, v[2]);
			if (!bDirect)
			{
				vNeiBo[v[1] - iBase].emplace_back(v[0] - iBase, v[2]);
			}
		}
		return vNeiBo;
	}	
	static vector<vector<int>> Mat(vector<vector<int>>& neiBoMat)
	{
		vector<vector<int>> neiBo(neiBoMat.size());
		for (int i = 0; i < neiBoMat.size(); i++)
		{
			for (int j = i + 1; j < neiBoMat.size(); j++)
			{
				if (neiBoMat[i][j])
				{
					neiBo[i].emplace_back(j);
					neiBo[j].emplace_back(i);
				}
			}
		}
		return neiBo;
	}
};

//堆(优先队列)优化迪杰斯特拉算法 狄克斯特拉(Dijkstra)算法详解
typedef pair<long long, int> PAIRLLI;
class  CHeapDis
{
public:
	CHeapDis(int n,long long llEmpty = LLONG_MAX/10):m_llEmpty(llEmpty)
	{
		m_vDis.assign(n, m_llEmpty);
	}
	void Cal(int start, const vector<vector<pair<int, int>>>& vNeiB)
	{
		std::priority_queue<PAIRLLI, vector<PAIRLLI>, greater<PAIRLLI>> minHeap;
		minHeap.emplace(0, start);
		while (minHeap.size())
		{
			const long long llDist = minHeap.top().first;
			const int iCur = minHeap.top().second;
			minHeap.pop();
			if (m_llEmpty != m_vDis[iCur])
			{
				continue;
			}
			m_vDis[iCur] = llDist;
			for (const auto& it : vNeiB[iCur])
			{
				minHeap.emplace(llDist + it.second, it.first);
			}
		}
	}
	vector<long long> m_vDis;
	const long long m_llEmpty;
};

class Graph {
		public:
			Graph(int n, vector<vector<int>>& edges):m_iN(n) {
				m_neiBo = CNeiBo::Three(n, edges, true);
			}
			void addEdge(vector<int> edge) {
				m_neiBo[edge[0]].emplace_back(edge[1], edge[2]);
			}

			int shortestPath(int node1, int node2) {
				CHeapDis m_dis(m_iN);
				m_dis.Cal(node1, m_neiBo);
				long long ret =  m_dis.m_vDis[node2];
				return (m_dis.m_llEmpty == ret) ? -1 : ret;
			}
			vector<vector<pair<int, int>>> m_neiBo;
			const int m_iN;
		};

单元测试

	vector<int> edges;
		TEST_METHOD(TestMethod11)
		{
			Graph g (4, vector<vector<int>> { {0, 2, 5}, {0, 1, 2}, {1, 2, 1}, {3, 0, 3} });
			auto res = g.shortestPath(3, 2); // 返回 6 。从 3 到 2 的最短路径如第一幅图所示:3 -> 0 -> 1 -> 2 ,总代价为 3 + 2 + 1 = 6 。
			AssertEx(6, res);
			res = g.shortestPath(0, 3); // 返回 -1 。没有从 0 到 3 的路径。
			AssertEx(-1, res);
			g.addEdge(vector<int>{ 1, 3, 4 }); // 添加一条节点 1 到节点 3 的边,得到第二幅图。
			res = g.shortestPath(0, 3); // 返回 6 
			AssertEx(6, res);
		}		

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

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


http://www.kler.cn/a/518577.html

相关文章:

  • Linux进程概念:【环境变量】【程序地址空间】
  • 【全栈】SprintBoot+vue3迷你商城(9)
  • Linux Futex学习笔记
  • 单片机基础模块学习——按键
  • Arcgis国产化替代:Bigemap Pro正式发布
  • 为AI聊天工具添加一个知识系统 之69 详细设计 之10 三种中台和时间度量 之2
  • 蓝桥杯3519 填充 | 分类讨论
  • 大型齿轮箱健康监测与智能维护系列套件:测试台+故障诊断算法工具箱+齿轮箱智能维护系统平台+案例分析
  • 数字MIC PDM接口
  • 【探索前端技术之 React Three.js—— 简单的人脸动捕与 3D 模型表情同步应用】
  • 【Web开发】一步一步详细分析使用Bolt.new生成的简单的VUE项目
  • LeetCode 力扣热题100 二叉树的直径
  • 使用 Python 和 Tesseract 实现验证码识别
  • ASP.NET Blazor托管模型有哪些?
  • Python数据分析-准备工作(一)
  • Electron 项目运行问题:Electron failed to install correctly
  • 172页满分PPT | 2024数据资产资本化知识地图
  • 乐理笔记——DAY01
  • JS高阶 - day02
  • Redis 的缓存穿透、缓存击穿和缓存雪崩是什么?如何解决?
  • Spring--AOP注解方式实现和细节
  • 使用缓存保存验证码进行登录校验
  • 【0x03】HCI_Connection_Complete事件详解
  • 【人工智能】大模型大算法迭代优化过程
  • 用css实现一个类似于elementUI中Loading组件有缺口的加载圆环
  • list对象获取最大的日期