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

news2025/1/13 14:27:39

在这里插入图片描述

文章目录

    • 图的邻接矩阵
  • 一.Floyd-Warshall算法思想(基于动态规划)
  • 二.Floyd-Warshall算法接口
  • 笔记附录:单源最短路径--Bellman-Ford算法
    • 1.Bellman-Ford算法接口核心部分
    • 2.Bellman-Ford算法接口

图的邻接矩阵

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个顶点:在这里插入图片描述

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

  • 从而有状态转移方程: Dist[i][j] = Dist[i][k] + Dist[k][j]在这里插入图片描述

  • 最短路径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算法接口

		//多源最短路径算法(允许带负权路径存在)
		//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为图的顶点数目)就可以遍历完所有的最短路径:在这里插入图片描述
  • 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;
		}

在这里插入图片描述

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

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

相关文章

网络安全(黑客)渗透测试学习路线

相信很多新手都会遇到以下几个问题 1.零基础想学渗透怎么入手&#xff1f; 2.学习web渗透需要从哪里开始&#xff1f; 这让很多同学都处于迷茫状态而迟迟不下手&#xff0c;就在此贴给大家说一下web渗透的学习路线&#xff0c;希望对大家有帮助 以下是免费的基础渗透测试入门…

关于vue3 使用 Echarts 绘制玫瑰图 (笔记)

目录 基于js 文件玫瑰图绘图 基于vue3 绘制玫瑰图 基于js文件绘图 // 定义一个配置对象 var option {// 图例设置legend: {top: bottom},// 工具栏设置toolbox: {show: true,feature: {mark: { show: true }, // 标记工具dataView: { show: true, readOnly: false }, // 数据视…

QA工具开发流程

前言 在项目上线前期&#xff0c;这边根据需求制作了一套QA测试工具。主要分为以下四个模块的测试**图1** **数值测试&#xff1a;**主要包括了角色的等级变更、游戏里货币的变更、&#xff08;目前已制作的&#xff09;游戏道具的数量变更。这些可能归一为一类测试模型**动画…

数据分析:理性消费下的小红书种草诀窍

导语 近期的茶饮界好生热闹&#xff0c;蜜雪冰城和瑞幸两大八竿子打不着的品牌&#xff0c;竟然也风风火火地组起了cp。 饮品界&#xff1a;IP大乱斗成流量密码 8月25日&#xff0c;蜜雪冰城的新动画片上线了&#xff0c;并且雪王贴心地选择了免费&#xff0c;爱优腾B站随便…

即时物流进入盈利期,为什么说顺丰同城才是“头雁”?

从餐饮店、便利店老板们扮演跑腿角色给顾客送商品算起&#xff0c;即时配送&#xff08;简称“即配”&#xff09;行业跌跌撞撞好几年&#xff0c;规模壮大、秩序提升&#xff0c;但盈亏平衡的及格线&#xff0c;始终让人望洋兴叹。直到这个夏天&#xff0c;平均分终于被拉上去…

Yjs + Quill 实现文档多人协同编辑器开发(基础+实战)

前言 多人协同开发确实是比较难的知识点&#xff0c;在技术实现上有一定挑战&#xff0c;但随着各种技术库的发展&#xff0c;目前已经有了比较成熟的解决方案。今介绍 Yjs 基于CRDT算法&#xff0c;用于构建自动同步的协作应用程序&#xff0c;与Quill富文本编辑器&#xff0c…

电影院放映厅订票选座系统 微信小程序

电影订票也是电影院的核心&#xff0c;是必不可少的一个部分。在电影院的整个影视行业中&#xff0c;影民担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类基于微信小程序也在不断改进。本课题所设计的电影订票小程序&#xff0c;使用微信开发者与java进行开…

wangluobiancheng

UDP send: receive: TCP

RK3588实战:调用npu加速,yolov5识别图像、ffmpeg发送到rtmp服务器

前言&#xff1a;最近在学习一些rk3588相关的东西&#xff0c;趁着这个项目&#xff0c;把学习的相关东西整合下&#xff0c;放到一个项目里面&#xff0c;巩固学习的知识。 项目名称&#xff1a;yolov5识别图像、ffmpeg发送到rtmp服务器 功能&#xff1a;1、opencv读取usb摄…

已解决 Python FileNotFoundError 的报错问题

本文摘要&#xff1a;本文已解决 Python FileNotFoundError 的相关报错问题&#xff0c;并总结提出了几种可用解决方案。同时结合人工智能GPT排除可能得隐患及错误。 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领…

第P3周:天气识别

一、前期准备 1、设置GPU import torch import torch.nn as nn import torchvision.transforms as transforms import torchvision from torchvision import transforms, datasetsimport os,PIL,pathlibdevice torch.device("cuda" if torch.cuda.is_available() …

【面经】字节测开实习岗面试题分享

1、自我介绍 2、为什么投了字节&#xff1f; 公司声誉&#xff1a;字节跳动是一家知名的科技公司&#xff0c;在互联网行业享有很高的声誉。 发展前景&#xff1a;字节跳动的业务涵盖了多个领域&#xff0c;包括短视频、资讯、教育等。这意味着您将有机会接触到不同的产品和…

有没有什么提醒软件是安全的?手机电脑同步的提醒软件

在繁忙的生活、工作和学习中&#xff0c;我们经常会因为繁琐的任务而忘记重要的事情。这时&#xff0c;一款可靠的提醒软件可以帮助我们高效地管理时间、提醒重要事项&#xff0c;例如提醒重要的会议、任务截止日期、提醒准时还款、重要纪念日等&#xff0c;避免错过重要事项。…

通过 Jetbrains GateWay实现Remote Development

本次环境准备 环境准备&#xff1a;win10、一台安装有树莓派系统的树莓派&#xff08;也可以是其他的服务器&#xff09; 第一步&#xff1a;通过官网下载JetBrains Gateway 官网地址&#xff1a;https://www.jetbrains.com/remote-development/gateway/ 第二步&#xff1a;安装…

Agile Management

Agile Management 敏捷管理

mac 安装 homebrew

摘要&#xff1a; 本文主要是下载安装包安装homebrew&#xff0c;然后配置环境变量Path。检验是否安装成功。 homebrew地址&#xff1a;macOS&#xff08;或 Linux&#xff09;缺失的软件包的管理器 — Homebrew 在终端命令下载安装&#xff1a; /bin/bash -c "$(curl…

数字孪生智慧仓储的关键特点和优势有哪些

数字孪生智慧仓储是一种基于数字孪生技术的智能仓储解决方案。数字孪生是指使用数字模型来模拟和仿真现实世界中的物理实体或系统的技术。在智慧仓储的上下文中&#xff0c;数字孪生被用来创建虚拟的仓储环境&#xff0c;以实时监测、优化和管理仓储操作。 数字孪生智慧…

【附源码】Python-3.9.5安装教程

软件下载 软件&#xff1a;Python版本&#xff1a;3.9.5语言&#xff1a;英文大小&#xff1a;26.9M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存2G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.baidu.com/…

性能测试项目案例

一、项目介绍与部署 1.1微商城功能介绍 轻商城是一个电商项目&#xff0c;需要综合评估各个项目各个接口的功能&#xff0c;给出优化建议&#xff1b; 功能框架 前台&#xff1a;首页、商品页详情、加入购物车、订单、支付、团购、优惠券&#xff1b;后台&#xff1a;商品管…

【MyBatisⅡ】动态 SQL

目录 &#x1f392;1 if 标签 &#x1fad6;2 trim 标签 &#x1f460;3 where 标签 &#x1f9ba;4 set 标签 &#x1f3a8;5 foreach 标签 动态 sql 是Mybatis的强⼤特性之⼀&#xff0c;能够完成不同条件下不同的 sql 拼接。 在 xml 里面写判断条件。 动态SQL 在数据库里…