求图的最短路径长度的弗洛伊德(Floyd)算法

弗洛伊德算法的适用情况:弗洛伊德算法既可以用来求解有向网的最短路径长度,也可以用来求无向网的最短路径长度,但是对于图中出现负权环的情况,弗洛伊德无法的得到正确的答案

弗洛伊德的算法思想:

以此图为例讲解弗洛伊德算法的算法步骤:

 

  1. 第一步将图用邻接矩阵的数据结构存储,得到一张二维表记作ArcInfor,顶点到其自身的边权值记为0,若两个顶点之间没有直达的边则即为无穷大,如图所示:对于图的邻接矩阵的存储结构不了解的读者可以去看我的这篇博客:http://t.csdn.cn/aQaTI这篇博客里面讲了图的基本知识以及图的顺序存储方式(邻接矩阵)以及链式存储方式(邻接表)的实现过程,就是有点长可能需要一点耐心才可以看的下去
  2. 第二步,以0作为中间顶点,求每一个顶点经过顶点0到达其他顶点的路径长度,若该长度小于上表表项,则更新上表,比如以0为中间结点,求顶点2途径中间顶点0到达顶点3的路径长度为:ArcInfor[2][0[+ArcInfor[0][3]=10>ArcInfor[2][3],因此不更新此表项
  3. 第三步就是重复第二步依次将其他顶点作为中间顶点,然后求每一个顶点途径此顶点到达其它顶点的路径长度,然后符合条件就更新表项,最终得到的二维表arc中就记录了任意两对顶点之间的最短路径的长度

从以上算法思想我们已经可以得到一个大致的思路就是弗洛伊德算法只需要借助简单的三重for循环就可以实现,如

void FloydAlgorithm(int v[V],int arc[V][V],int spath[V][V])
{

	//只需要用到三重for循环即可实现弗洛伊德算法

	for (int k = 0; k < V; k++)
	{
		for (int i = 0; i < V; i++)
		{
			for (int j = 0; j < V; j++)
			{
				if (arc[i][k] + arc[k][j] < arc[i][j])
				{
					arc[i][j] = arc[i][k] + arc[k][j];
					spath[i][j] = k;
				}

			}
		}
	}
	//打印最短路径
	print_spath(v, arc, spath);

}


 最外层循环控制变量是用于实现步骤“依次将每一个顶点作为中间顶点“,而内层的两层循环是用于实现”以某个顶点作为中间节点时,每一个顶点经过此结点到达其它顶点的最短路径“;

对于这题,我们不禁想要得到一个简单的最短路径长度,我们还期望得到任意两个顶点之间的最短路径,即将路径上途径的顶点输出;这里我们需要借助一个二维矩阵spath来实现,spath[i][j]的含义是,顶点i与j之间的中间顶点;输出对最短路径的代码如下,对于输出最短路径的代码,读者可能不是很清楚,这时候可以把我的代码调试一遍,观察各个变量取值的变化情况,这样你就能理解算法的设计思路了,对于我来说,我每次看不太懂别人的代码的时候用的都是此方法

//以下函数用于输出两个顶点之间的最短路径间需要经过那些顶点,不包括源节点和目的节点
void output(int v[V],int spath[V][V],int start,int end)         //start,end表示的是顶点所在的数组中的下标。
{
	//该函数需要用到递归思想,我们从右往左输出源节点到目的节点所经过的顶点的信息
	int k = spath[start][end];         //即从start到end中间需要经过的一个顶点再顶点数组中的下标

	if (k == -1)
	{
		return;
	}
	output(v, spath, start, k);
	printf("%d->", v[k]);
	output(v, spath, k, end);
}

//以下函数用于打印图中任意两个结点之间的最短路径
void print_spath(int v[V],int arc[V][V],int spath[V][V])
{
	for (int i = 0; i < V; i++)
	{
		for (int j = 0; j < V; j++)
		{
			if (i == j)
			{
				continue;
			}
			else
			{
				if (arc[i][j] >= INF)
				{
					printf("%d->%d之间不存在路径\n", v[i], v[j]);
				}
				else
				{
					printf("%d->%d之间的最短路径长度为:%d\n", v[i],v[j],arc[i][j]);
					printf("%d->%d之间的最短路径为:\n", v[i], v[j]);
					printf("%d->", v[i]);
					output(v, spath, i, j);
					printf("%d\n", v[j]);
				}
			}

		}
	}
}

完整程序源代码以及运行结果截图:

程序源代码:


//用弗洛伊德算法求图的最短路径

//弗洛伊德算法既可以用来求无向图的最短路径,也可以用来求有向图的最短路径;

//注意:弗洛伊德算法不可以用于求存在负权环的图的最短路径

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>


#define V 4     //顶点的数目 
#define INF 60000         //用于定义一个大数,当两个顶点之间不存在边或者弧时,在邻接矩阵中的对应位置上填上该大数,infinity:无穷大



//采用邻接矩阵的方式来存储图的边的信息



//以下函数用于输入图的边的顶点信息和边的信息
void input(int *v,int arc[V][V]);


//以下函数用于实现弗洛伊德算法

void FloydAlgorithm(int v[V], int arc[V][V], int spath[V][V]);


//以下函数用于输出两个顶点之间的最短路径间需要经过那些顶点,不包括源节点和目的节点
void output(int v[V], int spath[V][V], int start, int end);

//以下函数用于打印图中任意两个结点之间的最短路径
void print_spath(int v[V], int arc[V][V], int spath[V][V]);

int main()
{
	int vertex[V];         //用于存放图的顶点信息
	int ArcInfor[V][V];          //用于存放图的边的信息
	int spath[V][V];            //用于记录两个顶点之间的最短路径
	for (int i = 0; i < V; i++)
	{
		for (int j = 0; j < V; j++)
		{
			spath[i][j] = -1;
		}
	}
	//输入图的弧的信息和边的信息
	input(vertex, ArcInfor);

	//求最短路径
	FloydAlgorithm(vertex, ArcInfor, spath);

	return 0;


}


void input(int* v, int arc[V][V])
{
	printf("请输入图的顶点信息:\n");
	for (int i = 0; i < V; i++)
	{
		scanf("%d", &v[i]);
	}


	printf("请输入图的边的信息:\n");
	for (int i = 0; i < V; i++)
	{
		for (int j = 0; j < V; j++)
		{
			scanf("%d", &arc[i][j]);
			
		}
	}
	
}

//以下函数用于输出两个顶点之间的最短路径间需要经过那些顶点,不包括源节点和目的节点
void output(int v[V],int spath[V][V],int start,int end)         //start,end表示的是顶点所在的数组中的下标。
{
	//该函数需要用到递归思想,我们从右往左输出源节点到目的节点所经过的顶点的信息
	int k = spath[start][end];         //即从start到end中间需要经过的一个顶点再顶点数组中的下标

	if (k == -1)
	{
		return;
	}
	output(v, spath, start, k);
	printf("%d->", v[k]);
	output(v, spath, k, end);
}

//以下函数用于打印图中任意两个结点之间的最短路径
void print_spath(int v[V],int arc[V][V],int spath[V][V])
{
	for (int i = 0; i < V; i++)
	{
		for (int j = 0; j < V; j++)
		{
			if (i == j)
			{
				continue;
			}
			else
			{
				if (arc[i][j] >= INF)
				{
					printf("%d->%d之间不存在路径\n", v[i], v[j]);
				}
				else
				{
					printf("%d->%d之间的最短路径长度为:%d\n", v[i],v[j],arc[i][j]);
					printf("%d->%d之间的最短路径为:\n", v[i], v[j]);
					printf("%d->", v[i]);
					output(v, spath, i, j);
					printf("%d\n", v[j]);
				}
			}

		}
	}
}



void FloydAlgorithm(int v[V],int arc[V][V],int spath[V][V])
{

	//只需要用到三重for循环即可实现弗洛伊德算法

	for (int k = 0; k < V; k++)
	{
		for (int i = 0; i < V; i++)
		{
			for (int j = 0; j < V; j++)
			{
				if (arc[i][k] + arc[k][j] < arc[i][j])
				{
					arc[i][j] = arc[i][k] + arc[k][j];
					spath[i][j] = k;
				}

			}
		}
	}
	//打印最短路径
	print_spath(v, arc, spath);

}


 运行结果截图:

祝学习进步,生活愉快! 

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

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

相关文章

从git上拉取项目

目录 一、前期准备&#xff0c;获取git下载链接 二、idea下载 2.1.打开git下载界面 2.2.进入下载界面 2.3.下载前期配置 2.4.输入账号密码 2.5.下载完成后idea打开 2.6.下载完成后文件目录展示 三、命令行下载 3.1.打开所需要下载的项目路径 3.2.进入黑窗口 …

c#快速入门(下)

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析2 目录 &#x1f449;&#x1f3fb;Inline和lambda委托和lambda &#x1f449;&#x1f…

操作系统(2.8)--线程的实现

目录 线程的实现方式 1.内核支持线程(KST) 2.用户级线程(ULT) 3.组合方式 线程的实现 1.内核支持线程的实现 2.用户级线程的实现 线程的创建和终止 线程的实现方式 1.内核支持线程(KST) 内核支持线程&#xff0c;与进程相同&#xff0c;是在内核的支持下运行的&#x…

前端使用tailwindcss 快速实现主题切换方案

使用Tailwind CSS在黑暗模式下为你的网站设计样式。 现在&#xff0c;黑暗模式是许多操作系统的第一流功能&#xff0c;为你的网站设计一个黑暗版本以配合默认设计&#xff0c;变得越来越普遍。 为了使这一点尽可能简单&#xff0c;Tailwind包括一个暗色变体&#xff0c;让你…

JVM之类的初始化与类加载机制

类的初始化 clinit 初始化阶段就是执行类构造器方法clinit的过程。此方法不需定义&#xff0c;是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。构造器方法中指令按语句在源文件中出现的顺序执行。clinit不同于类的构造器。(关联&#xff1a;…

连锁药店系统:如何提高效率和客户满意度?

连锁药店系统是一种用于提高效率和客户满意度的软件系统&#xff0c;它能够管理多个药店的日常营运。通过这种系统&#xff0c;药店可以更好地管理库存、员工、销售和客户信息等方面&#xff0c;从而提高整体的经营效率。 首先&#xff0c;连锁药店系统能够帮助药店管理库存。系…

算法刷题总结 (十一) 二叉树

算法总结11 二叉树 一、二叉树的概念1.1、什么是二叉树&#xff1f;1.2、二叉树的常见类型1.2.1、无数值&#xff08;1&#xff09;、满二叉树&#xff08;2&#xff09;、完全二叉树 1.2.2、有数值&#xff08;3&#xff09;、二叉搜索树&#xff08;4&#xff09;、平衡二叉搜…

数字孪生与物流园区:优化布局规划的关键

随着全球贸易的增长和物流行业的发展&#xff0c;物流园区作为重要的物流枢纽和供应链管理中心&#xff0c;扮演着至关重要的角色。而数字孪生技术的出现为物流园区的运营和管理带来了革命性的变化。数字孪生技术是一种将实体物体与其数字化模型相结合的创新技术&#xff0c;通…

【UEFI】BIOS 阶段全局变量类型

BIOS的几个阶段需要不同阶段的数据传递&#xff0c;下面介绍4个全局变量。 1 固件存储介绍 本规范描述了应该如何在非易失性存储器中存储和访问文件。固件实现必须支持标准的PI固件卷和固件文件系统格式&#xff08;下文所述&#xff09;&#xff0c;但可能支持其他存储格式。…

什么是一致性哈希?一致性哈希是如何工作的?如何设计一致性哈希?

1.什么是一致性哈希&#xff1f;一致性哈希是如何工作的&#xff1f;如何设计一致性哈希&#xff1f;05-25 2.系统设计&#xff1a;从零用户扩展到百万用户05-28 收起 如果你有 n 个缓存服务器&#xff0c;一个常见的负载均衡方式是使用以下的哈希方法&#xff1a; 服务器索…

强连通分量-tarjan算法缩点

一. 什么是强连通分量&#xff1f; 强连通分量&#xff1a;在有向图G中&#xff0c;如果两个顶点u,v间&#xff08;u->v&#xff09;有一条从u到v的有向路径&#xff0c;同时还有一条从v到u的有向路径&#xff0c;则称两个顶点强连通(strongly connected)。如果有向图G的每…

NLP实战:调用Gensim库训练Word2Vec模型

目录 一、准备工作 1. 安装Gensim库 2. 对原始语料分词 二、训练Word2Vec模型 三、模型应用 1.计算词汇相似度 ​编辑 2. 找出不匹配的词汇 3. 计算词汇的词频 四、总结 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学…

Flask-RESTful的使用

Flask-RESTful的使用 Flask-RESTful基本使用安装定义资源Resources创建API实例添加资源到API运行Flask应用 请求处理请求解析参数校验 响应处理数据序列化定制返回格式 其他功能蓝图装饰器集合路由命名规范路由名称 Flask-RESTful Flask-RESTful是一个用于构建RESTful API的扩展…

C++类和对象 -- 知识点补充

补充 const成员函数static成员友元内部类匿名对象拷贝对象时的一些编译器优化 const成员函数 将const修饰的成员函数称为const成员函数&#xff0c;const修饰类成员函数&#xff0c;实际是修饰该成员函数隐含的this指针&#xff0c;表明在该成员函数中不能对类的成员进行修改。…

使用MockJS进行前端开发中的数据模拟

在前端开发中&#xff0c;有时我们需要在没有后端接口的情况下进行前端页面的开发和测试。这时&#xff0c;我们可以使用MockJS来模拟数据&#xff0c;以便进行开发和调试。MockJS是一个用于生成随机数据和拦截Ajax请求的JavaScript库&#xff0c;它能够帮助我们快速搭建起一个…

Linux---用户切换命令(su命令、sudo命令、exit命令)

1. su命令 root用户拥有最大的系统操作权限&#xff0c;而普通用户在许多地方的权限是受限的。 普通用户的权限&#xff0c;一般在其HOME目录内是不受限的。 一旦出了HOME目录&#xff0c;大多数地方&#xff0c;普通用户仅有只读和执行权限&#xff0c;无修改权限。 su 是…

【操作系统】01.操作系统概论

操作系统的发展历史 未配置操作系统 手工操作阶段 用户独占全机&#xff0c;人机速度矛盾导致系统资源利用率低 脱机输入输出方式 为了缓解主机cpu和IO设备之间速度不匹配的矛盾&#xff0c;出现了脱机IO技术 在外围机的控制下&#xff0c;通过输入设备&#xff0c;将数据输…

耗时1周整理了网络安全学习路线,非常详细!

前言 这一期就出一个怎么学习网络安全的学习路线和方法&#xff0c;觉得有用的话三连收藏下 首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有几门&#xff0c;有些人会倒在学习linux系统及命令的路上…

数论专题(3)逆元

目录 初步认识 逆元 定义 应用 费马小定理 好久没有更新我们的数论专题板块了&#xff0c;今天&#xff0c;我们就来探究一下新知——逆元。 初步认识 在数据非常大的情景下&#xff0c;我们通常会对数据先进行取模运算&#xff0c;来计算在一定的范围内进行处理。而运算…

Java 进阶 -- 集合(一)

本节描述Java集合框架。在这里&#xff0c;您将了解什么是集合&#xff0c;以及它们如何使您的工作更轻松&#xff0c;程序更好。您将了解组成Java Collections Framework的核心元素——接口、实现、聚合操作和算法。 介绍告诉您集合是什么&#xff0c;以及它们如何使您的工作…