图论算法基础:单源最短路径Dijkstra算法分析

news2025/5/21 2:06:11

在这里插入图片描述

文章目录

    • 图的邻接矩阵
  • 一.Dijkstra算法分析
    • 算法的核心逻辑要素
    • 算法的执行逻辑
  • 二.Dijkstra算法接口实现
    • 邻接矩阵堆优化版本:

图的邻接矩阵

namespace Graph_Structure
{
	//Vertex是代表顶点的数据类型,Weight是边的权值的数据类型,MAX_W是权值的上限值(表示不相两)
	//Direction表示图是否为有向图
	template<class Vertex, class Weight = int, Weight MAX_W = INT_MAX, bool Direction = false>
	class Graph
	{
		typedef Graph<Vertex, Weight, MAX_W, Direction> Self;
	public:
		//使用编译器的默认构造函数
		Graph() = default;

		//给定一个存放顶点的数组用来初始化图
		Graph(const Vertex* a, size_t n)
		{
			_vertexs.reserve(n);
			_indexMap.rehash(n);
			_matrix.resize(n, std::vector<Weight>(n, MAX_W));
			for (size_t i = 0; i < n; ++i)
			{
				_vertexs.push_back(a[i]);
				//建立顶点和数组下标的映射(目的是为了邻接矩阵的边存储)
				_indexMap[a[i]] = i;
			}
		}

		//获取顶点在邻接矩阵中对应的下标
		size_t GetVertexIndex(const Vertex& vertex)
		{
			if (_indexMap.find(vertex) == _indexMap.end())
			{
				throw "invalued_para";
				return -1;
			}
			return _indexMap[vertex];
		}


		void _AddEdge(size_t srci, size_t dsti, const Weight& w)
		{
			//判断是有向图还是无向图
			if (Direction == false)
			{
				_matrix[dsti][srci] = w;
			}
			_matrix[srci][dsti] = w;
		}
		//给定起点和终点,在邻接矩阵中添加一条边
		void AddEdge(const Vertex& src, const Vertex& dst, const Weight& w)
		{
			if (_indexMap.find(src) == _indexMap.end() || _indexMap.find(dst) == _indexMap.end())
			{
				throw "invalued_para";
			}

			size_t srci_index = GetVertexIndex(src);
			size_t dst_index = GetVertexIndex(dst);
			_AddEdge(srci_index, dst_index, w);
		}
		
		//将图的邻接矩阵打印出来
		void Print()
		{
			for (auto e : _vertexs)
			{
				std::cout << e << '[' << _indexMap[e] << ']' << std::endl;
			}

			std::cout << "     ";
			for (int i = 0; i < _vertexs.size(); ++i)
			{
				std::cout << i << "    ";
			}
			std::cout << std::endl;


			int i = 0;
			for (auto arry : _matrix)
			{
				std::cout << i++ << ' ';
				for (auto e : arry)
				{
					if (e == MAX_W)
					{
						printf("%4c ", '*');
					}
					else
					{
						printf("%4d ", e);
					}
				}
				std::cout << std::endl;
			}
		}

		//图的广度优先遍历
		void BFS(const Vertex& src)
		{
			size_t begin = GetVertexIndex(src);
			std::queue<int> QNode;
			std::vector<bool> Label(_vertexs.size(), false);
			QNode.push(begin);
			Label[begin] = true;
			size_t Level = 0;
			while (!QNode.empty())
			{
				size_t LevelSize = QNode.size();
				for (size_t i = 0; i < LevelSize; ++i)
				{
					size_t front = QNode.front();
					QNode.pop();
					std::cout << _vertexs[front] << '[' << front << ']' << std::endl;
					for (int j = 0; j < _vertexs.size(); ++j)
					{
						if (Label[j] == false && _matrix[front][j] != MAX_W)
						{
							QNode.push(j);
							Label[j] = true;
						}
					}
				}
			}
		}
		
		//图的深度优先遍历
		void DFS(const Vertex& src)
		{
			std::vector<bool> visited(_vertexs.size(), false);
			_DFS(GetVertexIndex(src), visited);
		}
	private:
		void _DFS(size_t srci, std::vector<bool>& visited)
		{
			visited[srci] = true;
			std::cout << _vertexs[srci] << '[' << srci << ']' << std::endl;
			for (int i = 0; i < _vertexs.size(); ++i)
			{
				if (_matrix[srci][i] != MAX_W && visited[i] == false)
				{
					_DFS(i, visited);
				}
			}
		}
	private:
		std::vector<Vertex> _vertexs;						// 顶点集合
		std::unordered_map<Vertex, size_t> _indexMap;		// 顶点映射下标
		std::vector<std::vector<Weight>> _matrix;			// 邻接矩阵
	};
}

有向带权图中给定一个起始顶点(源点),Dijkstra算法可以求出所有其他顶点到源点的最短路径,Dijkstra算法不能用于同时含有正负权值的边的图

一.Dijkstra算法分析

在这里插入图片描述

算法的核心逻辑要素

  1. Source顶点集合:已经确定到源点的最短路径的顶点就会加入Source集合中,Source集合初始时只有源点
  2. dist数组:用于记录每个顶点到源点的距离,dist数组具有如下性质:
    • 对于存在于Source集合中的顶点,该顶点在dist数组中对应的值为该顶点到源点最短路径的距离(一类顶点)
    • 对于不在Source集合中但是Source集合直接相连的顶点,该顶点在dist数组中对应的值为该顶点经过Source集合达到源点的最短路径的距离(二类顶点)
    • 对于不在Source集合中不与Source集合直接相连的顶点,该顶点在dist数组中对应的值为无穷大(三类顶点)
      在这里插入图片描述

算法的执行逻辑

  • 容易证明,dist数组只要保持上述的性质,那么,在二类顶点中,dist值最小的顶点就可以加入Source集合而且这个最小值就是该顶点到源点的最短距离.
  • 每当有一个顶点加入了Source集合,就以该顶点为枢纽对dist数组进行更新保持其原本的性质
  • 如此往复直到图中所有顶点都加入了Source集合,dist数组就记录了图中所有顶点到源点的最短距离.
    在这里插入图片描述

二.Dijkstra算法接口实现

在这里插入图片描述

		//单源最短路径算法,src为起始顶点(源点)
		//dist用于记录各顶点到源点的距离
		//parentPath用于记录最短路径中各顶点的前驱顶点
		void Dijkstra(const Vertex& src, std::vector<Weight>& dist, std::vector<int>& parentPath)
		{
			dist.resize(_vertexs.size(), MAX_W);
			parentPath.resize(_vertexs.size(), -1);
			//用于标记Source集合中顶点的表,初始时只有源点在其中
			std::vector<bool> Source(_vertexs.size(), false);
			int srcindex = GetVertexIndex(src);
			dist[srcindex] = 0;		//源点自己到自己的距离为0

			//图共有多少个顶点就执行多少次循环
			for (int i = 0; i < _vertexs.size(); ++i)
			{
				//从dist数组中选出距离源点最近的二类顶点加入到Source集合中
				int MinWeight = MAX_W;
				int Minindex = -1;
				for (int j = 0; j < dist.size(); ++j)
				{
					if (Source[j] == false && dist[j] < MinWeight)
					{
						MinWeight = dist[j];
						Minindex = j;
					}
				}
				//将顶点加入Source集合中
				Source[Minindex] = true;
				//更新与Source集合直接相连且不在Source集合中的顶点距离源点的距离(dist数组的更新)
				for (int j = 0; j < _matrix.size(); ++j)
				{
					if (_matrix[Minindex][j] != MAX_W &&
						Source[j] == false && _matrix[Minindex][j] + dist[Minindex] < dist[j])
					{
						dist[j] = _matrix[Minindex][j] + dist[Minindex];
						//记录顶点前驱
						parentPath[j] = Minindex;
					}
				}
			}
		}
  • 接口中的parentPath数组用于记录最短路径中每个顶点的前驱顶点,算法结束后,借助parentPath数组中可以完整地得到每一条最短路径

邻接矩阵堆优化版本:

  • dist数组中选出距离源点最近的二类顶点这个过程可以借助堆来完成,邻接矩阵堆优化版本:
		//堆优化版本
		struct vertex_dist
		{
			int _dest;
			Weight _v_source;

			vertex_dist(int dest,Weight v_source)
				:_dest(dest),
				 _v_source(v_source){}

			//小堆比较运算符
			bool operator>(const vertex_dist& v_d) const
			{
				return _v_source > v_d._v_source;
			}
		};
		void Dijkstra_heap(const Vertex& src, std::vector<Weight>& dist, std::vector<int>& parentPath)
		{
			dist.resize(_vertexs.size(), MAX_W);
			parentPath.resize(_vertexs.size(), -1);
			//用于标记Source集合中顶点的表,初始时只有源点在其中
			std::vector<bool> Source(_vertexs.size(), false);
			int srcindex = GetVertexIndex(src);
			dist[srcindex] = 0;		//源点自己到自己的距离为0
			//创建小堆
			std::priority_queue<vertex_dist, std::vector<vertex_dist>, std::greater<vertex_dist>> Heap;
			Heap.push(vertex_dist(srcindex, 0));

			while (!Heap.empty())
			{
				vertex_dist Smallest = Heap.top();
				Heap.pop();
				//若顶点已经在Source集合中则跳过
				if (Source[Smallest._dest]) continue;
				//将顶点加入Source集合中
				Source[Smallest._dest] = true;
				for (int i = 0; i < _vertexs.size(); ++i)
				{
					if (_matrix[Smallest._dest][i] != MAX_W &&
						Source[i] == false && _matrix[Smallest._dest][i] + dist[Smallest._dest] < dist[i])
					{
						dist[i] = _matrix[Smallest._dest][i] + dist[Smallest._dest];
						Heap.push(vertex_dist(i, dist[i]));
						//记录顶点前驱
						parentPath[i] = Smallest._dest;
					}
				}
			}
		}
  • 邻接矩阵堆优化版本并不能降低算法的时间复杂度(仍然为O(N^2)),要想降低Dijkstra算法的时间复杂度就要使用邻接表存储结构链式前向星存储结构(配合堆优化)可以将复杂度降为O(mlogn)( n表示点数,m 表示边数)
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/944236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目 - 后端技术栈转型方案

前言 某开发项目的后端技术栈比较老了&#xff0c;现在想换到新的技术栈上。使用更好的模式、设计思想、更合理的架构等&#xff0c;为未来的需求迭代做铺垫。怎么办呢&#xff1f;假设系统目前在线上运行着的&#xff0c;直接整体换的话耗时太久&#xff0c;且中间还有新的需…

【嵌入式】Keil5自带JLink识别不到芯片(unkown to this version of the jlink software)的处理

目录 一 问题现象 二 原因分析 三 问题处理 一 问题现象 使用了一款新的嵌入式芯片&#xff0c;灵动微MM32SPIN27PF&#xff0c;安装了官方提供的J-Link Pack支持包。 【1】直接使用 JLink_V694a 可以正常烧写程序&#xff1b; 【2】使用Keil5烧写失败&#xff0c;显示报错“…

demo的改进和完善(首页添加介绍和图片)

1.在首页添加介绍 将布局→ 区域→ 静态内容拖拽到body中→ 标题 更改为自己想要的名称→ 源 写入html代码 《学习demo》是用来记录我的学习过程中遇到的问题及解决方案&#xff0c;见证了我的进步和成长 <hr> 相关内容可见<a href"https://blog.csdn.net/clove…

你的住宅安全吗?这个技能赶紧学学

随着城市化的不断加速和人口增长&#xff0c;住宅小区的管理和安全问题也愈发凸显出来。在这种背景下&#xff0c;门禁监控系统成为了一种既有效又实用的解决方案。 门禁监控系统不仅可以控制和管理出入小区的人员和车辆&#xff0c;还可以提供实时监控和记录&#xff0c;为小区…

视频监控/视频汇聚/视频云存储EasyCVR平台HLS流集成在小程序无法播放的问题排查

安防视频/视频云存储/视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上&#xff0c;视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频…

实现 Trie (前缀树)

题目链接 实现 Trie (前缀树) 题目描述 注意点 word 和 prefix 仅由小写英文字母组成 解答思路 首先要理解前缀树是什么&#xff0c;参照该篇文章【图解算法】模板变式——带你彻底搞懂字典树(Trie树)在了解前缀树是什么后&#xff0c;设计前缀树就会更加容易&#xff0c;…

java八股文面试[多线程]——主内存和工作内存的关系

JAVA内存模型&#xff08;JMM&#xff09;共享变量&#xff1a;如果一个变量在多个线程的工作内存中都存在副本&#xff0c;那么这个变量就是这几个线程的共享变量。 上面的工作内存其实是java内存模型抽象出来的概念&#xff0c;下面简要介绍一下java内存模型&#xff08;JMM&…

正中优配:股票经手费必须交吗?

在股票出资中&#xff0c;经手费是一个不可避免的要素。那么&#xff0c;股票经手费有必要交吗&#xff1f;从多个视点来看&#xff0c;这个问题需求进行必定的剖析。 法令视点&#xff1a;股票经手费有必要交 从法令视点来看&#xff0c;股票经手费有必要交。依据《证券法》的…

SQL 语句继续学习之记录三

一&#xff0c;数据的插入&#xff08;insert 语句的使用方法&#xff09; 使用insert语句可以向表中插入数据(行)。原则上&#xff0c;insert语句每次执行一行数据的插入。 列名和值用逗号隔开&#xff0c;分别扩在&#xff08;&#xff09;内&#xff0c;这种形式称为清单。…

Llama模型结构解析(源码阅读)

目录 1. LlamaModel整体结构流程图2. LlamaRMSNorm3. LlamaMLP4. LlamaRotaryEmbedding 参考资料&#xff1a; https://zhuanlan.zhihu.com/p/636784644 https://spaces.ac.cn/archives/8265 ——《Transformer升级之路&#xff1a;2、博采众长的旋转式位置编码》 前言&#x…

安科瑞风力发电场集中监控系统解决方案-安科瑞黄安南

作为清洁能源之一&#xff0c;风力发电场近几年装机容量快速增长。8月17日&#xff0c;国家能源局发布1-7月份全国电力工业统计数据。截至7月底&#xff0c;全国累计发电装机容量约27.4亿千瓦&#xff0c;同比增长11.5%。其中&#xff0c;太阳能发电装机容量约4.9亿千瓦&#x…

Oracle数据传输加密方法

服务器端“dbhome_1\NETWORK\ADMIN\”sqlnet.ora文件中添加 SQLNET.ENCRYPTION_SERVER requested SQLNET.ENCRYPTION_TYPES_SERVER (RC4_256) 添加后新的链接即刻生效&#xff0c;服务器无需重新启动。 也可以通过Net manager管理工具添加 各个参数含义如下&#xff1a; 是…

Web开发模式、API接口、restful规范、序列化和反序列化、drf安装和快速使用

一 Web开发模式 1. 前后端混合开发模式 前后端混合开发模式是一种开发方式&#xff0c;将前端和后端的开发工作结合在一起&#xff0c;以加快项目的开发速度和 提高协作效率。这种模式通常用于快速原型开发、小型项目或敏捷开发中。在前后端混合开发模式中&#xff0c;前端和…

【MyBatis】自定义resultMap三种映射关系

目录 一、一对一映射&#xff08;One-to-One&#xff09; 1.1 表关系 1.2 resultMap设置自定义映射 二、一对多映射&#xff08;One-to-Many&#xff09; 2.1 创建实体 2.2 级联方式处理映射关系 2.3 定义SQL 2.4 OrderMapper接口 2.5 编写业务逻辑层 2.6 Junit测试…

港联证券:游资爆炒中电环保,还有谁在蹭核污染防治概念?

8月28日&#xff0c;核污染防治概念股持续大涨&#xff0c;建工修复&#xff08;300958.SZ&#xff09;、捷强配备&#xff08;300875.SZ&#xff09;、东方园林&#xff08;002310.SZ&#xff09;、华盛昌&#xff08;002980.SZ&#xff09;等涨停。 中小市值的概念股成为游资…

人工智能学习专栏

这个专栏就专门用来记录自己的深度学习的历程吧。从做MCU开始、Soc、Linux系统转行到AI领域&#xff0c;其过程是痛苦的。至少数学这块&#xff0c;那是花了很多时间去从头去学。但是还是有很多不懂的地方。坚持&#xff01;&#xff01;&#xff01;&#xff01;

03 最长连续序列

最长连续序列 题解 哈希(O(n)) 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 题解 哈希(O(n)) class Solution { public:int long…

升级iOS17后iPhone无法连接App Store怎么办?

最近很多用户反馈&#xff0c;升级最新iOS 17系统后打开App Store提示"无法连接"&#xff0c;无法正常打开下载APP。 为什么升级后无法连接到App Store&#xff1f;可能是以下问题导致&#xff1a; 1.网络问题导致App Store无法正常打开 2.网络设置问题 3.App Sto…

微信报修系统有什么优势?怎么提升企业维修工作效率与管理水平?

随着智能化时代的到来&#xff0c;企业、事业单位的现代化设备数量和种类不断增加&#xff0c;原本繁琐的报修、填写记录、检修管理等工作得以简化。从发起报修到维修&#xff0c;以及维修之后给予评价的整个过程&#xff0c;通过手机微信报修系统均能看到&#xff0c;既省时又…

算法---二叉树中的最大路径和

题目 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root &…