图论算法基础:单源最短路径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算法分析

图论算法基础:单源最短路径Dijkstra算法分析,图论数据结构,算法,图论

算法的核心逻辑要素

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

算法的执行逻辑

  • 容易证明,dist数组只要保持上述的性质,那么,在二类顶点中,dist值最小的顶点就可以加入Source集合而且这个最小值就是该顶点到源点的最短距离.
  • 每当有一个顶点加入了Source集合,就以该顶点为枢纽对dist数组进行更新保持其原本的性质
  • 如此往复直到图中所有顶点都加入了Source集合,dist数组就记录了图中所有顶点到源点的最短距离.
    图论算法基础:单源最短路径Dijkstra算法分析,图论数据结构,算法,图论

二.Dijkstra算法接口实现

图论算法基础:单源最短路径Dijkstra算法分析,图论数据结构,算法,图论文章来源地址https://www.toymoban.com/news/detail-680306.html

		//单源最短路径算法,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 表示边数)
    图论算法基础:单源最短路径Dijkstra算法分析,图论数据结构,算法,图论

到了这里,关于图论算法基础:单源最短路径Dijkstra算法分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法提高-图论-单源最短路的扩展应用

    多源点单终点最短路建图: 创建虚拟源点(创建虚拟源点的时候以是spfa为例 可以在建图的时候建出来,也可以在spfa这直接入队,也是虚拟源点的意思) 反向建图变成单源点多终点,然后遍历终点的dist即可找出最短路 这题挺简单的就不详细说了,主要是第一次遇到计数问题

    2024年02月16日
    浏览(36)
  • Dijkstra算法——单源最短路径(指定一个节点(源点)到其余各个顶点的最短路径)

    国庆期间,小明打算从1号城市出发,在五天假期中分别去往不同的城市(2,3,4,5,6)旅游,为减轻负担,他想要知道1号城市到各个城市之间的最短距离。 现在需要设计一种算法求得源点到任意一个城市之间的最短路径。该问题的求解也被称为“单源最短路径”。 在所有

    2024年02月03日
    浏览(41)
  • C++ | 数据结构与算法 | 单源最短路径 | Dijkstra && Bellman Ford

    (关于代码实现的图结构,可以看图结构的实现这篇文章) Dijkstra的实现与Prim的实现相似,两者都是通过贪心思想实现,它们有什么不同呢?首先Prim算法是针对无向图的最小生成树的,而Dijkstra算法是针对 有向图 的最短路径生成。一个的目的是连接图中的所有顶点,生成一

    2024年02月03日
    浏览(37)
  • 算法提高-图论-单源最短路的建图方式

    建图 找出一个牧场,它到其他牧场的距离之和最小 我是这么理解的,djsktra是一个贪心的思想,加法里面不能加负数我就不说了 求乘法最大值的时候为什么边权必须0-1,因为在乘法最大值里面有一个边权大于1的话那不就等价于求加法最小值的时候有一个边权为负数的么,d

    2024年02月08日
    浏览(31)
  • 【算法入门&图论】【模板】拓扑排序|【模板】单源最短路2 |最小生成树

    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人 ✨精品专栏:C++面向对象 🔥系列专栏:算法百炼成神 本专栏收录的均为牛客网的算法题目,内含链表、双指针、递归、动态规划、基本数据结构等算法思想的具体运用。牛客网不仅有大量的经典算法题目,也有大厂的面

    2024年02月03日
    浏览(34)
  • 【数据结构】最小生成树(Prim算法,普里姆算法,普利姆)、最短路径(Dijkstra算法,迪杰斯特拉算法,单源最短路径)

    问题解答 (1)最小生成树(Minimal Spanning Tree)的定义 生成树的代价 :设 G ( V , E ) G(V,E) G ( V , E ) 是一个无向连通网图,生成树上 各边的权值之和 称为 生成树的代价 。 最小生成树 :在图 G G G 所有生成树中, 代价最小的生成树 为 最小生成树 。 (2)最小生成树(MST)的性

    2024年02月11日
    浏览(31)
  • 【图论】单源最短路

    算法提高课笔记 最短路问题可以分为以下两类: 边权非负——朴素Dijkstra、堆优化Dijkstra 有负权边——Bellman-Ford、SPFA 热浪 原题链接 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!! 他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品

    2024年02月14日
    浏览(27)
  • 【图论】单源最短路问题

    Dijkstra算法是一种单源最短路径算法,用于找出图中从一个源点到其他所有点的最短路径。该算法的原理是采用贪心策略,每次将距离源点最近的点加入到已确定最短路径的集合中,并更新其它节点的距离。具体实现过程如下: 初始化距离数组dist[],源点距离为0,其余点距离

    2024年02月13日
    浏览(26)
  • 单源最短路径(spfa,Dijkstra, bellman-ford)

    目录  Dijkstra 原理:基于贪心。 为什么 Dijkstra 不能处理有负边的情况 Bellman-ford 原理:动态规划, 实质见floyd的另一篇博客 1,能找负环, 2,有变数限制的最短路径 spfa 原理 spfa怎么求负环, 原理:基于贪心。 第一步 初始化距离,dist[1] = 0, 一号点到起点的距离为0, 其他点

    2024年02月04日
    浏览(38)
  • 【图论 单源最短路】100276. 最短路径中的边

    单源最短路 图论知识汇总 给你一个 n 个节点的无向带权图,节点编号为 0 到 n - 1 。图中总共有 m 条边,用二维数组 edges 表示,其中 edges[i] = [ai, bi, wi] 表示节点 ai 和 bi 之间有一条边权为 wi 的边。 对于节点 0 为出发点,节点 n - 1 为结束点的所有最短路,你需要返回一个长度

    2024年04月22日
    浏览(22)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包