Dijkstra算法详解

本文详细介绍了Dijkstra算法,包括其目标、起源、图的基本概念、算法的数学表示及类型,以及算法的多语言实现。Dijkstra算法用于寻找图中节点间的最短路径,由Edsger W. Dijkstra在1959年提出。文中还通过实例演示了算法工作原理,并提供了C、C++、Python和Java的实现示例。
摘要由CSDN通过智能技术生成

最近在计算机网络这门课中学到了Dijkstra算法,因此做了以下详细整理以便初学者都能理解。

算法目标和使用场景

使用 Dijkstra 算法,可以寻找图中节点之间的最短路径。特别是,可以在图中寻找一个节点(称为“源节点”)到所有其它节点的最短路径,生成一个最短路径树。

Dijkstra算法起源

1959 年,荷兰杰出计算机科学家、软件工程师 Dr. Edsger W. Dijkstra创建了该算法,并发表了一篇 3 页的文章《A note on two problems in connexion with graphs》来介绍他的新算法。

1994年,Edsger Dijkstra博士在ETHZurich(Andreas F. Borchert摄)

1994年,Edsger Dijkstra博士在ETHZurich(Andreas F. Borchert摄)

在 2001 年的一次采访中,Dijkstra 博士透露了他设计这个算法的起因和过程:

从 Rotterdam 到 Groningen 的最短路线是什么?我花了大概 20 分钟时间设计了这个寻找最短路径的算法。一天早上我正和我年轻的未婚妻在 Amsterdam 逛街,觉得有点累了,我们就坐在咖啡厅的露台上喝了一杯咖啡,我在想是否能够解决这个问题,然后,我设计出了这个最短路径算法。我说过,这是一个 20 分钟的设计。事实上,三年之后的 1959 年它才被发布,现在看来依然很不错,其原因之一是我当时设计的时候没有纸和笔,从而不得不极力避免所有可避免的复杂性。最终,令我惊讶的是,这个算法成为了我成名的基石之一。
——引自文章《An interview with Edsger W. Dijkstra》.

图的基本概念

是用于表示元素与元素之间存在某种关系的结构。

  • 这些元素称为节点,它们表示现实生活中的对象、人或实体。
  • 节点之间的连接称为

图的图形表示

下面是“图”的图形表示:

https://www.freecodecamp.org/news/content/images/2020/06/image-123.png

图的数学表示

  • 图(Graph):G
  • 节点(Node):N
  • 边(Edge):E

图的类型

有向与无向图

图可以是:

  • 无向图: 在互相连接的节点之间可以以任意方向移动。
  • 有向图: 在互相连接的节点之间只能以特定的方向移动。使用带单向箭头的线来表示有向边。

https://www.freecodecamp.org/news/content/images/2020/06/image-124.png

权重图

权重图的边是带有“权重”的,边的权重可以表示距离、时间或其他能够以节点之间的“连接”表示的概念。

下面的权重图中,每个边旁边都有一个蓝色数字表示其权重。

https://www.freecodecamp.org/news/content/images/2020/06/image-126.png

Dijkstra 算法详解视频

视频搬运自B站,强烈建议先看视频帮助理解!

【算法】最短路径查找—Dijkstra算法

Dijkstra 算法示例

理解了算法概念之后,通过逐步的示例来了解一下它背后的工作原理。

假设有下面这个图:

https://www.freecodecamp.org/news/content/images/2020/06/image-76.png

Dijkstra 算法将会寻找出图中节点 0 到所有其他节点的最短路径。

我们将会得到节点 0 到节点 1、节点 0 到节点 2、节点 0 到 节点 3……(以此类推)的最短路径。

初始的距离列表如下:

  • 源节点到它自身的距离为 0。示例中的源节点定为节点 0,不过你也可以选择任意其它节点作为源节点。
  • 源节点到其它节点的距离还没有确定,所以先标记为无穷大∞。

https://www.freecodecamp.org/news/content/images/2020/06/image-77.png

还有一个列表用来记录哪些节点未被访问(即尚未被包含在路径中):

https://www.freecodecamp.org/news/content/images/2020/06/image-78.png

💡 提示: 记住,当所有节点都被添加到路径中时,算法的计算过程就完成了。

我们选择了从节点 0 出发,可以直接将它标记为“已访问”,同样的,在未访问节点列表中把它划掉,并在图中给它加上红色的边框:

https://www.freecodecamp.org/news/content/images/2020/06/image-87.png

https://www.freecodecamp.org/news/content/images/2020/06/image-83.png

现在需要检查节点 0 到相邻节点的距离,两个相邻节点分别是节点 1 和节点 2(注意看红色的边):

https://www.freecodecamp.org/news/content/images/2020/06/image-89.png

这并不是说立即把这两个相邻节点加入到最短路径中。在把一个节点加入到最短路径之前,需要确认是否已经寻找到了访问它的最短路径。现在只是在对可选方案做初步检查。

更新节点 0 到节点 1、节点 0 到节点 2 的距离为它们之间的边的权重,分别为 2 和 6:

https://www.freecodecamp.org/news/content/images/2020/06/image-90.png

更新了到相邻节点的距离之后:

  • 根据已知的距离列表选择距离源节点最近的节点。
  • 将它标记为“已访问”。
  • 将它添加到路径中。

查看距离列表,发现节点 1 到源节点的距离是最短的(距离为 2),所以把它加入到路径中。

在图中,以红色边来表示:

https://www.freecodecamp.org/news/content/images/2020/06/image-94.png

在距离列表中用红色方块标记这个节点,表明它是“已访问”的、已经寻找到了访问这个节点的最短路径:

https://www.freecodecamp.org/news/content/images/2020/06/image-92.png

在未访问节点列表中将它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-93.png

现在分析新的相邻节点,寻找访问它们的最短路径。只需要分析已经在最短路径(标记为红色边)中的节点的相邻节点。

节点 2 和节点 3 都是最短路径包含的节点的相邻节点,因为它们分别与节点 0 和节点 1 直接相连,如下图所示。下一步将要分析这两个节点。

https://www.freecodecamp.org/news/content/images/2020/06/image-94.png

之前已经计算过源节点到节点 2 的距离,并记录在了列表中,所以不用更新。这次只需要更新源节点到新的相邻节点(节点 3)的距离:

https://www.freecodecamp.org/news/content/images/2020/06/image-98.png

这个距离是 7,来看看为什么。

为了计算源节点到另一个节点(这里指节点 3)的距离,需要把访问该节点的最短路径的所有边权重相加:

  • 对于节点 3 将构成路径 0 -> 1 -> 3 的所有边权重相加,得到总距离为 70 -> 1 距离为 2,1 -> 3 距离为 5)。

https://www.freecodecamp.org/news/content/images/2020/06/image-94.png

现在得到了到相邻节点的距离,需要选择一个节点添加到路径中。我们必须选择一个已知到源节点距离最短的未访问节点。

从距离列表中可以看出,距离为 6 的节点 2 就是我们的选择:

https://www.freecodecamp.org/news/content/images/2020/06/image-98.png

在图中为它加上红色边框,并将路径上的边标记为红色:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fPkaZ3O-1675673228629)(null)]

在距离列表中用红色方块把它标记为“已访问”,在“未访问”节点列表中把它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-99.png

https://www.freecodecamp.org/news/content/images/2020/06/image-100.png

重复前面的步骤,寻找源节点到新的相邻节点节点 3 的最短路径。

可以看到,有两种可选的路径:0 -> 1 -> 30 -> 2 -> 3。一起看看我们是如何确定最短路径的。

https://www.freecodecamp.org/news/content/images/2020/06/image-96.png

节点 3 在之前已经有了一个距离记录(距离为 7,参阅下表),这个距离是之前步骤中由路径 0 -> 1 -> 3 的两个边权重(分别为 5 和 2)相加得到的。

不过现在有了一个新的可选路径:0 -> 2 -> 3,它途经权重分别为 68 的两条边 0 -> 22 -> 3,总距离为 14

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DuZuzmYA-1675673229741)(null)]

显然,第一个路径的距离更短(7 vs. 14),所以选择第一个路径 0 -> 1 -> 3只有在新的路径距离更短的情况下,才会更新距离列表。

因此,使用第一种方案 0 -> 1 -> 3,将节点添加到路径中。

https://www.freecodecamp.org/news/content/images/2020/06/image-104.png

把这个节点标记为“已访问”,在“未访问”节点列表中把它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-103.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jU2nMQPi-1675673227471)(null)]

重复前面的过程。

检查尚未访问的相邻节点:节点 4 和节点 5,因为它们是节点 3 的相邻节点。

https://www.freecodecamp.org/news/content/images/2020/06/image-104.png

更新它们到源节点的距离,尝试寻找更短的路径:

  • 对于节点 4 路径是 0 -> 1 -> 3 -> 4,距离为 17
  • 对于节点 5 路径是 0 -> 1 -> 3 -> 5,距离为 22

💡 提示: 注意我们只能从最短路径(红色边)上进行扩展,而不能途经未被包含在最短路径中的边(例如,不能构造经过边 2 -> 3 的路径)。

https://www.freecodecamp.org/news/content/images/2020/06/image-105.png

现在需要选择将哪个未访问节点标记为“已访问”,这里选择节点 4,因为在距离列表中它的距离最短。在图中做标记:

https://www.freecodecamp.org/news/content/images/2020/06/image-108.png

在距离列表中用红色方块将它标记为“已访问”:

https://www.freecodecamp.org/news/content/images/2020/06/image-107.png

在“未访问”节点列表中把它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-109.png

再次重复前面的过程。检查相邻节点:节点 5 和节点 6。分析每一种从已访问节点到它们之间的可能路径方案。

https://www.freecodecamp.org/news/content/images/2020/06/image-108.png

对于节点 5

  • 第一种选择是路径 0 -> 1 -> 3 -> 5,到源节点的距离为 22(2 + 5 + 15),前面的步骤已经记录了这个距离。
  • 第二种选择是路径 0 -> 1 -> 3 -> 4 -> 5,到源节点的距离为 23(2 + 5 + 10 + 6)。

显然,第一个路径距离更短,为节点 5 选择第一种方案。

对于节点 6

  • 可选的路径是 0 -> 1 -> 3 -> 4 -> 6,到源节点的距离为 19(2 + 5 + 10 + 2)。

https://www.freecodecamp.org/news/content/images/2020/06/image-110.png

把距离最短(当前已知)的节点 6 标记为“已访问”。

https://www.freecodecamp.org/news/content/images/2020/06/image-140.png

在“未访问”节点列表中把它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-111.png

现在得到了如下路径(标记为红色):

https://www.freecodecamp.org/news/content/images/2020/06/image-112.png

现在只剩下一个节点 5 还没被访问了,看看我们要如何把它添加到路径中。

从已经添加到路径中的节点出发,有三种不同的路径可以访问节点 5

  • 第一种选择: 0 -> 1 -> 3 -> 5,总距离为 22(2 + 5 + 15)。
  • 第二种选择: 0 -> 1 -> 3 -> 4 -> 5,总距离为 23(2 + 5 + 10 + 6)。
  • 第三种选择: 0 -> 1 -> 3 -> 4 -> 6 -> 5,总距离为 25(2 + 5 + 10 + 2 + 6)。

https://www.freecodecamp.org/news/content/images/2020/06/image-113.png

选择总距离为 22 的最短路径:0 -> 1 -> 3 -> 5

https://www.freecodecamp.org/news/content/images/2020/06/image-115.png

把这个节点标记为“已访问”,并在“未访问”节点列表中把它划掉:

https://www.freecodecamp.org/news/content/images/2020/06/image-114.png

https://www.freecodecamp.org/news/content/images/2020/06/image-116.png

瞧! 我们得到了从节点 0 到图中每个节点的最短路径。

https://www.freecodecamp.org/news/content/images/2020/06/image-115.png

图中,标记为红色的边表示最短路径:连接节点 0 和目标节点的红色边即为从源节点出发访问目标节点的最短路径。

例如,想要从节点 0 出发访问节点 6,连接它们的红色边就是最短路径,跟着走就行了。

Dijkstra算法做题演示

Untitled

Dijkstra算法多语言实现

C实现

#include <stdio.h>
#define MAX_VERtEX_NUM 20                   //顶点的最大个数
#define VRType int                          //表示弧的权值的类型
#define VertexType int                      //图中顶点的数据类型
#define INFINITY 65535
typedef struct {
   
    VertexType vexs[MAX_VERtEX_NUM];        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值