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

在这里插入图片描述

文章目录

    • 图的邻接矩阵
  • 一.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 表示边数)
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

bpmnjs Properties-panel拓展(属性设置篇)

最近有思考工作流相关的事情&#xff0c;绘制bpmn图的工具认可度比较高的就是bpmn.js了&#xff0c;是一个基于node.js的流程图绘制框架。初始的框架只实现了基本的可视化&#xff0c;想在xml进行客制化操作的话需要拓展&#xff0c;简单记录下几个需求的实现过程。 修改基础 …

ZLMediaKit 重建docker包

1.下载容器到本地服务器并运行 #此镜像为github持续集成自动编译推送&#xff0c;跟代码(master分支)保持最新状态 docker run -id -p 1935:1935 -p 8080:80 -p 8443:443 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 9000:9000/udp zlmediakit/zlmedi…

第六章:数据结构与算法-par1:典型数据结构

文章目录 一、典型数据结构介绍1.1 基本概念和术语1、基本数据概念2、抽象数据类型3、算法4、算法复杂度5、数据结构 二、数据的存储结构2.1 线性结构1、线性表&#xff08;一般线性表&#xff09;2、栈和队列&#xff08;受限线性表&#xff09;1) 栈 Stack2&#xff09; 队列…

Linux下套接字TCP实现网络通信

Linux下套接字TCP实现网络通信 文章目录 Linux下套接字TCP实现网络通信1.引言2.具体实现2.1接口介绍1.socket()2.bind()3.listen()4.accept()5.connect() 2.2 服务器端server.hpp2.3服务端server.cc2.4客户端client.cc 1.引言 ​ 套接字(Socket)是计算机网络中实现网络通信的一…

ATA-2161高压放大器的电子实验案例(案例合集)

ATA-2161是一款理想的可放大交直流信号的单通道高压放大器。最大差分输出1600Vp-p(800Vp)高压&#xff0c;可以驱动高压型负载。凭借其优异的指标参数受到不少电子工程师的喜欢&#xff0c;其在电子实验中的应用也非常频繁&#xff0c;下面为大家整理出ATA-2161高压放大器的应用…

Android全面屏下,默认不会全屏显示,屏幕底部会留黑问题

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 公司以前的老项目&#xff0c;便出现了这种情况&#xff0c;网上搜索了各种资料&#xf…

LVS集群 (四十四)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、集群概述 1. 负载均衡技术类型 2. 负载均衡实现方式 二、LVS结构 三、LVS工作模式 四、LVS负载均衡算法 1. 静态负载均衡 2. 动态负载均衡 五、ipvsadm命令详…

docker高级(redis集群三主三从)

1. 新建6个docker容器redis实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381docker run -d --name redis-node-2 --net host --privilegedtrue -v /…

Matlab图像处理-平移运算

几何运算 几何运算又称为几何变换&#xff0c;是将一幅图像中的坐标映射到另外一幅图像中的新坐标位置&#xff0c;它不改变图像的像素值&#xff0c;只是改变像素所在的几何位置&#xff0c;使原始图像按照需要产生位置、形状和大小的变化。 图像几何运算的一般定义为&#…

STM32使用PID调速

STM32使用PID调速 PID原理 PID算法是一种闭环控制系统中常用的算法&#xff0c;它结合了比例&#xff08;P&#xff09;、积分&#xff08;I&#xff09;和微分&#xff08;D&#xff09;三个环节&#xff0c;以实现对系统的控制。它的目的是使 控制系统的输出值尽可能接近预…

JVM——内存模型

1.java内存模型 1.1 原子性 1.2 问题分析 这里与局部变量自增不同&#xff0c;局部变量调用iinc是在局部变量表槽位上进行自增。 静态变量是在操作数栈自增。 这里的主内存和工作内存时再JMM里的说法。 因为操作系统是时间片切换的多个线程轮流使用CPU. 1.3解决方法 JMM中…

从项目中突显技能:在面试中讲述你的编程故事

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

hadoop 学习:mapreduce 入门案例一:WordCount 统计一个文本中单词的个数

一 需求 这个案例的需求很简单 现在这里有一个文本wordcount.txt&#xff0c;内容如下 现要求你使用 mapreduce 框架统计每个单词的出现个数 这样一个案例虽然简单但可以让新学习大数据的同学熟悉 mapreduce 框架 二 准备工作 &#xff08;1&#xff09;创建一个 maven 工…

24个非常实用的Python小技巧

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 1.唯一性 以下方法可以检查给定列表是否有重复的地方&#xff0c;可用set&#xff08;&#xff09;的属性将其从列表中删除。 x [1,1,2,2,3,2,3,4,5,6] y [1,2,3,4,5] len(x) len(set(x)) # False len(y) len(set(y)) # Tr…

基于LOF算法的异常值检测

目录 LOF算法简介Sklearn官网LOF算法应用实例1Sklearn官网LOF算法应用实例2基于LOF算法鸢尾花数据集异常值检测读取数据构造数据可视化&#xff0c;画出可疑异常点LOF算法 LOF算法简介 LOF异常检测算法是一种基于密度的异常检测算法&#xff0c;基于密度的异常检测算法主要思想…

Vue项目中app.js过大,导致web初始化加载过慢问题

1、删除多余不需要的库&#xff1a; npm uninstall xxx 如例如moment库文件是很大的可以直接放到index.html文件直接CDN引入 2、修改/config/index.js配置文件&#xff1a;将productionGzip设置为false ​ 3、设置vue-router懒加载 懒加载配置&#xff1a; ​ 非懒加载配置&…

基于PIC单片机篮球计分计时器

一、系统方案 本设计采用PIC单片机作为主控制器&#xff0c;矩阵键盘控制&#xff0c;比分&#xff0c;计时控制&#xff0c;24秒&#xff0c;液晶12864显示。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 2、液晶显示程序 /*************…

【JSDocvscode】使用JSDoc、在vscode中开启node调试、使用vscode编写运行Python程序

JSDoc JSDoc是JavaScript的一种注释语法&#xff0c;同时通过JSDoc注释也可以规避js弱类型中不进行代码提示的问题 图形展示JSDoc的效果&#xff1a; 上述没有进行JSDoc&#xff0c;然后我们a点什么 是没有任何提示的 上述就是加上 JSDoc的效果 常用的 vscode 其实内置了 js…

使用apifox前置数据base64编码并添加一个字段

具体前置脚本如下&#xff1a; // pm.request.body.update 处理 body 参数里的变量 let bodyStr pm.request.body.raw; // base64 编码数据 let bodyEncode btoa(bodyStr); console.log(bodyEncode) let newBody {"data": bodyEncode,"sendTime": &qu…

linux 设置与命令基础(二)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、系统基本操作 二、命令类型 三、命令语法 四、命令补齐 五、命令帮助 六、系统基本操作命令 总结 前言 这是本人学习Linux的第二天&#xff0c;今天主…