最短路径(Dijkstra算法与Floyd算法)

大话数据结构-迪杰斯特拉算法Dijkstra)和弗洛伊德算法Floyd 最短路径,迪杰斯特拉算法Dijkstra)、弗洛伊德算法Floyd)基本原理及邻接矩阵的代码实现。 阅读详情

一、Dijkstra算法

Dijkstra算法与之前学习过的Prim算法有些相似之处。我们直接通过一个例子来讲解

假设要求的是A->E之间的最短路径。首先我们来列出顶点A到其他各顶点的路径长度:A->D = 2,A->B = 6,A->C = 1,A->E = ∞。既然是要寻找最短路径,我们当然是先在已有的路径里面挑一条最短的,也就是A->C。将到达过的顶点用红色进行标识

到达C点后,我们又可以找到两条路径:C->B = 5,C->E = 7。此时我们拿这几条新的路径长度,与之前的A->C = 1相加,就可以得到A->B = 6,A->E = 8。出现了一条比之前短的路径:A->E = 8。所以我们将其更新到之前的路径列表里:A->D = 2,A->B = 6,A->C = 1,A->E = 8

接下来再从路径列表里选择一条最短的路径,也就是:A->D = 2。将D标记为已到达。

到达D点后,又可以找到两条新的路径:D->B = 3,D->E = 6。与A->D = 2相加可得:A->B = 5,A-E = 8。又有一条比之前短的路径:A->B = 5,所以将其更新到路径列表:A->D = 2,A->B = 5,A->C = 1,A->E = 8

再选择一条最短的路径:A->B = 5。将B标记为已到达。

这次只有一条新的路径:B->E = 2。将其与A->B = 5相加得:A->E = 7。比之前的路径更短,所以更新到路径列表:A->D = 2,A->B = 5,A->C = 1,A->E = 7。最后来到E点,结束遍历。

与此同时,我们获得了一张从A到各个顶点的最短路径长度表。

代码如下

public struct Path
{
	// 上一顶点下标
	public int preNode;
	// 起始顶点到当前顶点的总路径长度
	public int totalWeight;

	public Path(int preNode, int totalWeight)
	{
		this.preNode = preNode;
		this.totalWeight = totalWeight;
	}
}

/// <summary>  
/// Dijkstra算法  
/// </summary>  
/// <param name="graph"></param>  
/// <param name="startIndex"></param>
public Path[] Dijkstra<T>(GraphByAdjacencyMatrix<T> graph,int startIndex)
{
	// 用来存储顶点是否访问过
	bool[] flags = new bool[graph.Count];
	// 存储起始顶点到其他各顶点的最短路径长度
	Path[] paths = new Path[graph.Count];

	// 初始化起始顶点到其他顶点的路径
	for (int i = 0; i < graph.Count; i++)
	{
		paths[i] = new Path(startIndex, graph.Matrix[startIndex, i]);
	}
	// 将初始顶点设为已访问
	flags[startIndex] = true;
	int minIndex = 0;
	
	for (int i = 1; i < graph.Count; i++)
	{
		int min = Int32.MaxValue;
		// 寻找已存储路径中的最短路径
		for (int j = 0; j < graph.Count; j++)
		{
			if (!flags[j] && paths[j].totalWeight < min)
			{
				min = paths[j].totalWeight;
				minIndex = j;
			}
		}
		
		// 将最近的顶点设为已访问
		flags[minIndex] = true;
		// 基于当前顶点,查找更远顶点的最短路径
		for (int j = 0; j < graph.Count; j++)
		{
			// 把min放在右侧防止溢出
			if (!flags[j] && graph.Matrix[minIndex, j] < paths[j].totalWeight - min)
			{
				paths[j].preNode = minIndex;
				paths[j].totalWeight = min + graph.Matrix[minIndex, j];
			}
		}
	}

	return paths;
}

/// <summary>
/// 输出最短路径
/// </summary>
/// <param name="graph"></param>
/// <param name="startIndex"></param>
/// <param name="endIndex"></param>
private void PrintShortestPathByDijkstra<T>(GraphByAdjacencyMatrix<T> graph,int startIndex,int endIndex)
{
	var paths = Dijkstra<T>(graph, startIndex);
	var stack = new Stack<T>(graph.Count);
	int curIndex = endIndex;
	
	while (curIndex != startIndex)
	{
		stack.Push(graph.Nodes[curIndex]);
		curIndex = paths[curIndex].preNode;
	}
	stack.Push(graph.Nodes[startIndex]);
	while (stack.Count > 0)
	{
		Console.Write(stack.Pop());
		if(stack.Count != 0)
			Console.Write("->");
	}
	
	Console.Write(" 路径长度="+paths[endIndex].totalWeight);
}

代码部分也与Prim算法的代码高度相似。引用一位知乎大佬的话来说,Prim算法更新的是未标记集合到已标记集合之间的距离;而Dijkstra算法更新的是原点到未标记集合之间的距离。

二、Floyd算法

与Dijkstra算法相比,Floyd算法就显得比较“暴力”了。我们来看下面这张图

假设我们要求从A到B的最短路径。首先最直观的路径就是A直接到B:A->B = 9。但假如我们找一个中间节点,也就是C,就会发现A->C->B = 7要比A->B = 9更短。因此现在A到B的最短路径更新为:A->C->B = 7

接下来看C点。C到B的直连路径为:C->B = 3。但是如果找中间节点D,就会发现C->D->B = 2C->B = 3更短。因此C到B的最短路径更新为:C->D->B = 2

最终,A到B的最短路径就是:A->C->D->B = 6

这就是Floyd算法的核心思想。

下面来通过一个具体的例子讲解算法流程。首先需要准备两个二维数组,weights数组用来存储各顶点之间的路径长度,paths数组用来存储路径的中间节点。在初始状态下,weights数组初始化为与图的邻接矩阵一致,paths数组初始化为路径的尾结点(如A->B的中间节点设置为B)。

首先,以A节点为中间节点,计算其他两个顶点经过A点相连的路径和,并与直连的路径相比较。如果经过A节点的方案更短,则更新到这两个矩阵中。比如B->C = 5,如果经过A节点则B->A->C = 7,路径变长了,所以不需要更新。又如C->D = ∞,如果经过A节点,则C->A->D = 3,路径变短了,所以需要更新。

接下来再以B为中间节点进行计算。

以此类推,直到整个矩阵全部更新完。最终的结果如下

现在假如我们要找A到E的最短路径。从weights数组可得A到E的最短路径长度为7。然后根据paths求得具体路径。首先根据paths[0][4] = 3可知,从A到E需要经过D点,也就是A->D。然后再根据paths[3][4] = 1可知,从D到E需要先经过B点,即A->D->B。再根据paths[1][4] = 4可知,从B到E可以直达,即A->D->B->E

代码如下

/// <summary>
/// Floyd算法
/// </summary>
/// <param name="graph"></param>
public Path[,] Floyd<T>(GraphByAdjacencyMatrix<T> graph)
{
	// 这里可以复用前面的Path结构体
	Path[,] paths = new Path[graph.Count,graph.Count];
	
	// 将路径长度初始化为与邻接矩阵一致
	// 将路径中间点初始化为边的尾结点
	for (int i = 0; i < graph.Count; i++)
	{
		for (int j = 0; j < graph.Count; j++)
		{
			paths[i, j].totalWeight = graph.Matrix[i, j];
			paths[i, j].preNode = j;
		}
	}

	// 依次寻找经过中间节点小于直达的路径
	for (int nodeIndex = 0; nodeIndex < graph.Count; nodeIndex++)
	{
		for (int row = 0; row < graph.Count; row++)
		{
			for (int col = 0; col < graph.Count; col++)
			{
				// 求和转换为求差防止溢出
				if (paths[row, col].totalWeight - paths[nodeIndex,col].totalWeight > paths[row,nodeIndex].totalWeight )
				{
					paths[row, col].totalWeight =
						paths[row, nodeIndex].totalWeight + paths[nodeIndex, col].totalWeight;
					paths[row, col].preNode = nodeIndex;
				}
			}
		}
	}
	return paths;
}

/// <summary>
/// 输出最短路径
/// </summary>
/// <param name="graph"></param>
/// <param name="startIndex"></param>
/// <param name="endIndex"></param>
private void PrintShortestPathByFloyd<T>(GraphByAdjacencyMatrix<T> graph,int startIndex,int endIndex)
{
	var paths = Floyd<T>(graph);
	int curIndex = startIndex;
	
	while (curIndex != endIndex)
	{
		Console.Write(graph.Nodes[curIndex]+"->");
		curIndex = paths[curIndex, endIndex].preNode;
	}
	Console.Write(graph.Nodes[endIndex]);
	Console.Write(" 路径长度="+paths[startIndex,endIndex].totalWeight);
}

三、参考资料

[1].《大话数据结构》
[2]. https://zhuanlan.zhihu.com/p/87748517
[3]. https://zhuanlan.zhihu.com/p/72248451

数据结构知识整理16最短路径 最短路径 在一个无权图中,从一个顶点到另一个顶点可能存在多条路径,路径长度不同,把路径长度最短(即经过边数最少)的那条路径称为最短路径,其路径长度叫做最短路径长度或最短距离。对于有权图,应考虑路径上各边的权值,则通常把一条路径上所经过边的权值之和定义为该路径的路径长度或称带权路径长度。从源点到终点可能不止一条路径,把带权路径长度最短的那条路径称为最短路径,其路径长度(权值之和)称为最短路径长度或... 阅读详情

相关推荐

面试官问我FloydDijkstra怎么选?一张图讲清区别、复杂度应用场景

本文详细对比了FloydDijkstra算法最短路径问题中的核心思想、复杂度应用场景。Floyd算法采用动态规划解决多源最短路径问题,适合稠密图和小规模全节点计算;Dijkstra算法基于贪心策略处理单源最短路径,适用于稀疏图和实时路径规划。通过性能对比和实战案例,帮助开发者在面试和工程中做出明智选择。

weixin_33423874的博客 270

最短路径问题—Dijkstra算法Floyd算法

最短路径问题一般分为两类:一是单源最短路径,即求图中某一顶点到其他顶点的最短路径,可通过Dijkstra(迪杰斯特拉)算法求解;二是求每一对顶点间的最短路径,可通过Floyd( 弗洛伊德)算法求解。

24考研人晴落的博客 5171

详解BFS,Dijkstra算法Floyd算法是如何解决最短路径问题的

目录1.BFS算法2.Dijkstra算法3.Floyd算法4.总结G纲是个物流离散中心,经常需要往各个城市运东西,怎么运送距离最近——单源最短路径问题各个城市之间也学要来往,相互之间怎么走距离最近?——每对顶点之间的最短路径如下图,BFS算法是如何实现最短路径问题的呢?设从顶点2开始,第一次搜索的结点为1号结点和6号结点,路径为1,从1号结点和6号结点开始找相邻的接地,5号结点和3号7号为相邻的结点,然后5号结点周围都是已经访问过的,3号结点和7号结点分别搜索搭配4号和8号结点,路径为4 代码 B

莫浅子的博客 1万+

Dijkstra算法Floyd算法详解(MATLAB代码)

详细讲解Dijkstra算法Floyd算法+MATLAB代码

lihaiyuan_0324的博客 5428

最短路径——Dijkstra算法Floyd算法

本文介绍图论中求最短路径Dijkstra算法Floyd算法,并给出了C语言代码实现。

仅以此博客记录日常学习工作中所思所得,水平有限,难登大雅,万望海涵。 2541

图的应用——最短路径Dijkstra 算法Floyd 算法

参照2025年王道的数据结构考研复习指导书对Dijkstra算法求单源最短路径问题和Floyd算法求各顶点之间最短路径问题的过程进行了详细的介绍。

kusunoki的博客 7691

最短路径算法DijkstraFloyd

本文介绍了两种经典的最短路径算法Dijkstra算法Floyd算法Dijkstra算法适用于单源最短路径问题,采用贪心策略,时间复杂度为O((V+E)logV),但不能处理负权边。Floyd算法则计算所有顶点对间的最短路径,采用动态规划方法,时间复杂度为O(),可处理负权边但不支持负权环。文章通过代码示例、流程图和对比表格详细说明了两种算法的原理、实现和适用场景,帮助读者根据实际问题需求选择合适的算法解决方案。最后建议读者动手实践以加深理解。

从不水文,坚持创作,加油 1504

最短路径问题(Dijkstra算法Floyd算法

最短路径问题(Dijkstra算法Floyd算法) 将近一天的时间(其实中间大部分看手机去了)终于把最短路径这个经典问题算搞懂了吧 一、Dijkstra算法 简单介绍: Dijkstra算法(即迪杰斯特拉算法)时间复杂度(O(n^2))主要就是解决从单一源点到其他各点的最短路径问题; 基本思路: 设置两个顶点的集合:S和T(S+T=V)(也就是说S和T集合的元素个数的和为:总结点个数),集合...

serendipity的博客 2815

最短路径——Dijkstra算法Floyd算法

一、Dijkstra算法 1、单源点的最短路径问题:给定带权有向图G和源点v,求从v到G中其余各顶点的最短路径。 我们用一个例子来具体说明迪杰斯特拉算法的流程。 定义源点为 0,dist[i]为源点 0 到顶点 i 的最短路径。其过程描述如下: 步骤 dist[1] dist[2] dist[3] dist[4] 已找到的集合 第 1 步 8 ...

凌晨的咸鱼的博客 4万+

数据结构之——最短路径Dijkstra算法Floyd算法

最短路径图论中一个很经典的问题:给定图G(V,E),求一条从起点到终点的路径,使的这条路径上经过的所有边的边权之和最小。 1.Dijkstra算法 Dijkstra算法(迪杰斯特拉)用来解决单源最短路径问题,即给定图G和起点s,通过算法得到s到达其他顶点的最短距离。Dijkstra的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择起点s的最短距离最小的一个顶点(记为u),访问并加入集合S,之后,令顶点u为中介点,优化起点s所有从u能到达的顶点v之间的最短距离,这样的

xiaoyong5854的博客 3685

图的最短路径Dijkstra算法Floyd算法

文章目录

薛定谔的猫的博客 1481

Dijkstra算法Floyd算法简介(最短路径算法

Dijkstra算法1.定义概览Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构图论,运筹学等等。注意该算法要求图中不存在负权边。问题描述:在无向图 G=(V,E) 中,假设每条边 E

在努力! 1377

Floyd算法&&Dijkstra算法

Floyd算法Dijkstra算法 一、Floyd算法 1、简要介绍:****Floyd算法又称插点法,是利用动态规划的思想寻找有权图多源点之间最短路径算法算法目的是寻找从点i到点j的最短路径。 2、步骤: 以此图为例 Floyd算法核心就是大循环N次下,循环所有点作为一个中转点f,去比较点i到点j的距离和点i到点f的距离加上点f到点j的距离。将较小的值作为点i到点j的距离。 如此图,先从 点1(i = 1)开始,若将点2(k = 2)作为中转点,想要计算的点为点3(j = 3),比较<1,3

yeller_Chen的博客 2214

【坐在马桶上看算法算法6:只有五行的Floyd最短路算法

暑假,小哼准备去一些城市旅游。有些城市之间有公路,有些城市之间则没有,如下图。为了节省经费以及方便计划旅程,小哼希望在出发之前知道任意两个城市之前的最短路程。 上图中有4个城市8条公路,公路上的数字表示这条公路的长短。请注意这些公路是单向的。我们现在需要求任意两个城市之间的最短路程,也就是求任意两个点之间的最短路径。这个问题这也被称为“多源最短路径”...

weixin_30487201的博客 9022

最短路径的长度

一、求出最短路径的长度   以下没有特别说明的话,dis[u][v]表示从u到v最短路径长度,w[u][v]表示连接u,v的边的长度。 1.Floyed-Warshall算法 O(N3)   简称Floyed(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。Floyed的时间复杂度是O (N3),适用于出现负边权的情况 ...

nihaibeizhanyong的博客 3112

深入理解DijkstraFloyd算法

本文还有配套的精品资源,点击获取 简介:Dijkstra算法Floyd算法图论中的两个重要算法,用于解决图中的最短路径问题。Dijkstra算法适合处理单源最短路径问题,无法处理负权边,而Floyd算法则可以解决所有顶点对之间的最短路径问题,包括处理负权边的情况。两种算法分别适用于不同的场景,Dijkstra算法的时间复杂度依赖于边和顶点的数量,而Floyd算法的时间复...

weixin_31720909的博客 1783

算法——求最短路径Dijkstra算法

图解Dijkstra算法,让你了解Dijkstra算法的每一步是怎么进行的

在夜里行走的博客 13万+
上一篇: UGUI性能优化学习笔记(番外)一些零星的优化点
下一篇: Unity Animancer插件(一)基本使用
夜槿笙歌
博客等级 码龄7年 173粉丝 82原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值