多源最短路径算法:Floyd-Warshall算法分析

这篇具有很好参考价值的文章主要介绍了多源最短路径算法:Floyd-Warshall算法分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划

图的邻接矩阵

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;			// 邻接矩阵
	};
}

在有向赋权图中(可以存在带负权值的路径,但不能存在总权值为负数的环路),Floyd-Warshall算法可以求出任意两个顶点间的最短路径

一.Floyd-Warshall算法思想(基于动态规划)

  • 假设图中有N个顶点,顶点按照0~N-1进行编号

  • 算法中使用二维数组Dist来记录点与点之间的最短路径长度,Dist[i][j]表示第i个顶点到第j个顶点的最短路径长度,Dist数组的初始状态图的邻接矩阵的拷贝

  • 任意两个顶点ij之间的最短路径上可能存在0 ~ N-2个顶点:多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划

  • 假设顶点i到顶点j的最短路径上编号最大的顶点k顶点,ik之间的路径为p1,kj之间的路径为p2(不难证明,p1同时也是顶点i到顶点k的最短路径,p2同时也是顶点k到顶点j的最短路径)

  • 从而有状态转移方程: Dist[i][j] = Dist[i][k] + Dist[k][j]多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划

  • 最短路径p1p2也可以按照相同的方式划分出子路径.重复路径划分,直到将路径划分成不能再被分割的各个最小状态,从各个最小状态开始进行状态转移就可以得到顶点i到顶点j的最短路径.

  • 状态转移任意两点的最短路径的过程可以通过如下循环完成:

			//动态规划求最优解
			for (int k = 0; k < _vertexs.size(); ++k)
			{
				for (int i = 0; i < _vertexs.size(); ++i)
				{
					for (int j = 0; j < _vertexs.size(); ++j)
					{
						if (Dist[i][k] != MAX_W && Dist[k][j] != MAX_W &&
							Dist[i][k] + Dist[k][j] < Dist[i][j])
						{
							Dist[i][j] = Dist[i][k] + Dist[k][j];
						}
					}
				}
			}

多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划

  • 其他任意两点的最短路径的确定过程也是类似的

二.Floyd-Warshall算法接口

		//多源最短路径算法(允许带负权路径存在)
		//Dist数组用于记录顶点间的最短路径的长度
		//ParentPath数组用于记录最短路径上某个顶点的前驱结点编号
		void FloydWarShall(std::vector<std::vector<Weight>>& Dist, std::vector<std::vector<int>>& ParentPath)
		{
			Dist.resize(_vertexs.size(), std::vector<Weight>(_vertexs.size(), MAX_W));
			ParentPath.resize(_vertexs.size(), std::vector<int>(_vertexs.size(), -1));

			//根据图的邻接矩阵初始化Dist数组
			for (int i = 0; i < _matrix.size(); ++i)
			{
				for (int j = 0; j < _matrix.size(); ++j)
				{
					if (i == j)
					{
						Dist[i][j] = 0;
					}
					else if(_matrix[i][j] != MAX_W)
					{
						Dist[i][j] = _matrix[i][j];
						ParentPath[i][j] = i;
					}
				}
			}

			//动态规划求各个最短路径
			for (int k = 0; k < _vertexs.size(); ++k)
			{
				for (int i = 0; i < _vertexs.size(); ++i)
				{
					for (int j = 0; j < _vertexs.size(); ++j)
					{
						if (Dist[i][k] != MAX_W && Dist[k][j] != MAX_W &&
							Dist[i][k] + Dist[k][j] < Dist[i][j])
						{
							Dist[i][j] = Dist[i][k] + Dist[k][j];
							//i到j最短路径上,j顶点的前驱为k到j最短路径上j的前驱
							ParentPath[i][j] = ParentPath[k][j];
						}
					}
				}
			}
		}

笔记附录:单源最短路径–Bellman-Ford算法

  • Bellman-Ford算法可以在带负权路径的图中求解单源最短路径的问题
  • 一维数组Dist用于记录源点到其他顶点的最短路径的长度:Dist[i]表示源点到i号结点的最短路径长度
  • 一维数组ParentPath数组用于记录最短路径上某个顶点的前驱结点编号:ParentPath[i]表示在最短路径上,第i号结点的前驱结点的编号

1.Bellman-Ford算法接口核心部分

			for (int i = 0; i < _vertexs.size() - 1; ++i)
			{
				for (int j = 0; j < _vertexs.size(); ++j)
				{
					for (int k = 0; k < _vertexs.size(); ++k)
					{
						if (_matrix[j][k] != MAX_W && dist[j] != MAX_W &&
							_matrix[j][k] + dist[j] < dist[k])
						{
							dist[k] = _matrix[j][k] + dist[j];
							parentPath[k] = j;
						}
					}
				}
  • 可以证明:上面的循环可以遍历任何一条可能存在的最短路径.对于任意一条最短路径,内部的双层循环至少可以记录下最短路径上的一条边,因此最外层循环只要进行N-1次(N为图的顶点数目)就可以遍历完所有的最短路径:多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划
  • Bellman-Ford算法需要检验图中是否存在总权值为负数的环路,存在总权值为负数的环路的图无法求解最短路径问题

2.Bellman-Ford算法接口

		//带负权路径的单源最短路径算法
		bool BellmanFord(const Vertex& src, std::vector<Weight>& dist, std::vector<int>& parentPath)
		{
			dist.resize(_vertexs.size(), MAX_W);
			parentPath.resize(_vertexs.size(), -1);

			int srci = GetVertexIndex(src);
			dist[srci] = Weight();
			bool flag = true;
			for (int i = 0; i < _vertexs.size() - 1; ++i)
			{
				for (int j = 0; j < _vertexs.size(); ++j)
				{
					for (int k = 0; k < _vertexs.size(); ++k)
					{
						if (_matrix[j][k] != MAX_W && dist[j] != MAX_W &&
							_matrix[j][k] + dist[j] < dist[k])
						{
							//经过j结点,更新源点到k结点的路径长度
							dist[k] = _matrix[j][k] + dist[j];
							parentPath[k] = j;
							flag = false;
						}
					}
				}
				if (flag)
				{
					//路径不再发生更新,则说明所有最短路径都已经确定
					return false;
				}
				flag = true;
			}

			//检验图中是否存在负权环路
			//如果存在负权环路,则Dist数组会继续被更新
			flag = false;
			for (int j = 0; j < _vertexs.size(); ++j)
			{
				for (int k = 0; k < _vertexs.size(); ++k)
				{
					if (_matrix[j][k] != MAX_W && dist[j] != MAX_W &&
						_matrix[j][k] + dist[j] < dist[k])
					{
						dist[k] = _matrix[j][k] + dist[j];
						flag = true;
					}
				}
			}
			return flag;
		}

多源最短路径算法:Floyd-Warshall算法分析,图论数据结构,算法,数据结构,动态规划文章来源地址https://www.toymoban.com/news/detail-707846.html

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

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

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

相关文章

  • 算法——图论——最短路径——Floyd / 传递闭包

    目录  Floyd-Warshall(弗洛伊德)算法 传递闭包 一、试题 算法训练 盾神与离散老师2    求所有顶点到所有顶点的最短路径问题 弗洛伊德算法(Floyd-Warshall algorithm)是一种用于寻找图中所有顶点对之间最短路径的动态规划算法。 该算法可以处理带有负权边但不含负权环的加权

    2024年02月20日
    浏览(38)
  • Floyd(多源汇最短路)

    给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,边权可能为负数。 再给定 k 个询问,每个询问包含两个整数 x 和 y,表示查询从点 x 到点 y 的最短距离,如果路径不存在,则输出  impossible 。 数据保证图中不存在负权回路。 输入格式 第一行包含三个整

    2024年02月12日
    浏览(27)
  • 图论:最短路(dijkstra算法、bellman算法、spfa算法、floyd算法)详细版

    终于是学完了,这个最短路我学了好几天,当然也学了别的算法啦,也是非常的累啊。 话不多说下面看看最短路问题吧。 最短路问题是有向图,要求的是图中一个点到起点的距离,其中我们要输入点和点之间的距离,来求最短路。 下面分为几类题目: 单源汇最短路--一个起

    2024年01月21日
    浏览(41)
  • 【算法基础:搜索与图论】3.4 求最短路算法(Dijkstra&bellman-ford&spfa&Floyd)

    关于最短路可见:https://oi-wiki.org/graph/shortest-path/ 无向图 是一种 特殊的 有向图。(所以上面的知识地图上没有区分边有向还是无向) 关于存储:稠密图用邻接矩阵,稀疏图用邻接表。 朴素Dijkstra 和 堆优化Dijkstra算法的 选择就在于图 是 稠密的还是稀疏的。 算法步骤: 有一

    2024年02月16日
    浏览(41)
  • Floyd - Warshall (弗洛伊德算法)

    图中任意两点之间的最短路径问题 Dijkstra和Bellman-Ford也可以以所有点为源点,求出任意两点之间的最短距离,但是Dijstra不能解决带负权的的边,Bellman-Ford 效率慢点 Floyd算法考虑的是一条最短路径的中间节点,即简单路径p={v1 , v2,  ...  ,vn}上除v1和vn的任意节点 设K是p的一个

    2024年02月06日
    浏览(37)
  • UVa247 Calling Circles(Floyd warshall算法)

    给定两个人相互打电话,如果a打给b,b打给c,c打给a,则说a,b,c在同一电话圈中。给出n个人的m次通话,输出所有的电话圈 用graph[u][v]=1表示u和v之间有打电话。在使用floyd算法计算所有的点对之间的值。graph[u][v]=1表示u,v之间有直接或者间接打电话。如果graph[u][v] = 1并且graph[v][u]

    2024年02月12日
    浏览(36)
  • 【C++ 实现】图论概念,最小生成树,单/多源最短路径实现

    首先节点的存取,V是节点key,vectorpairV,V map;其实已经能表达一个图了,但是这样保存节点对我们使用来说会导致复杂度高。 常用保存节点的方式,有矩阵和邻接表。 矩阵的优点:O(1) 时间找到两点是否相连以及他们的权值。 矩阵的缺点:找一点相邻的所有节点的时候是O(N

    2024年02月13日
    浏览(33)
  • 图论 - 最短路(Dijkstra、Bellman-Ford、SPFA、Floyd)

    单源:在边权正数时,稠密图用朴素Dijkstra,稀疏图用堆优化Dijkstra;存在负权边时,一般用SPFA,但是如果限制在k步内,则用Bellman-Ford。多源:只有Floyd,这个由于时间复杂度太高,在算法比赛中很少遇见。 1.问题描述 给定一个 n 个点 m 条边的有向图,图中可能存在重边和自

    2024年04月14日
    浏览(37)
  • 第三章 图论 No.3 flody之多源汇最短路,传递闭包,最小环与倍增

    flody的四个应用: 多源汇最短路 传递闭包 找最小环 恰好经过k条边的最短路 倍增 多源汇最短路:1125. 牛的旅行 1125. 牛的旅行 - AcWing题库 直径概念:同一连通块中,两个距离最远的点之间的距离 如何求直径?由于图中存在着两个连通块,所以直接对全图做一个flody,就能更

    2024年02月14日
    浏览(44)
  • 图论算法基础:单源最短路径Dijkstra算法分析

    在 有向带权图 中给定一个起始顶点(源点),Dijkstra算法可以求出 所有其他顶点 到源点的最短路径,Dijkstra算法 不能用于同时含有正负权值的边的图 Source 顶点集合:已经确定 到源点的最短路径 的顶点就会加入 Source 集合中, Source 集合初始时只有源点 dist 数组:用于记录每个顶点到

    2024年02月11日
    浏览(42)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包