Dijkstra是典型最短路径算法,计算一个起始节点到路径中其他所有节点的最短路径的算法和思想。在一些专业课程中如数据结构,图论,运筹学等都有介绍。其思想是一种基础的求最短路径的算法,通过基础思想的变化可以解决很多复杂问题,如导航线路,动态规划等。

Dijkstra 算法思想介绍

如下图是一个多节点,多路径图。下面以该图为例子讲解dijkstra算法寻找最短路径的过程。

以A点为起始点,求A点到其他点 B C D E F 5个点的最短路径,最后得出A到其他点的最短路径。

因为要求A到其他5个点的最短距离,所以构造一个数组记录A到B C D E F 5个点的路径距离。约定:

  • 如果A能够直接达到节点,则使用路径长度即权值作为其距离
  • 如果A节点不能直接达到节点则使用无穷大表示A到该点距离。
  • 任何点到自身都为0

那么在最开始时,A点到图中所有点的距离数组如下:

A B C D E F
0 10 无穷大 4 无穷大 无穷大

dijkstra的算法思想是从以上最短距离数组中每次选择一个最近的点,将其作为下一个点,然后重新计算从起始点经过该点到其他所有点的距离,更新最短距离数据。已经选取过的点就是确定了最短路径的点,不再参与下一次计算。

可能看到这里你完全不明白dijkstra算法的思想,心里可能想:这是说的人话吗?不要紧,如果算法一句话就能解释清楚,那就不会出现那么多算法书了。下面我们就从实际的选取过程中理解这个思想的精髓。

第一次选取

构建好的数组是这样的:

A B C D E F
0 10 无穷大 4 无穷大 无穷大

第一步选取该最短路径数组中值最小的一个点。因为A点到本身不需要参与运算,所以从剩下的点中选择最短的一个是D。

第二步以A-D的距离为最近距离更新A点到所有点的距离。即相当于A点经过D点,计算A到其他点的距离。

A-A : 0

A-B : A-D-B:6

A-C : A-D-C:19

A-D : A-D:4

A-E : A-D-E:10

A-F : A-D-F:去穷大

A B C D E F
0 6 19 4 10 无穷大

将现在A到各个点的距离和之前的比较,到相同点取最小值。更新了B C E的距离,得到如下新的最短距离数组:

A B C D E F
0 6 19 4 10 无穷大

同时现在A D两点已经计算过,不参与下面的计算。

第二次选取

第二次选取的数组为第一次中更新过最短距离的数组

A B C D E F
0 6 19 4 10 无穷大

第一步:因为A D 不参与选取,所有从剩下的点中选取最近距离是点B

第二步:以B为最新点,更新最短数组

A-A : 0

A-B : A-D-B:6

A-C : A-D-B-C:14

A-D : A-D:4

A-E : A-D-B-E:12

A-F : A-D-B-F:无穷大

A B C D E F
0 6 14 4 12 无穷大

对比现在的最短距离和上一个数组的距离,到相同节点取最小的,C点由19更新成14,E点走A-D-E为10,距离更短所以不更新(敲黑板,这个重要),得到如下数组:

A B C D E F
0 6 14 4 10 无穷大

此时B点加入最短路径范围中。

第三次选取

上一步得到的数组为:

A B C D E F
0 6 14 4 10 无穷大

第一步:选取除了A B D节点之外的剩余节点中最短节点,为点E

第二步:以E点为最新节点,更新最短路径数组



因为在上一部中计算达到E点的距离时没有更新距离,A-D-E 为10 最短,所以更新E点到B C F点的距离时走的路径是A-D-E。注意这里的最短距离有对应的路径,选择最小值就是选择最短距离。

A-A : 0

A-B : A-D-B:6

A-C : A-D-E-C:11

A-D : A-D:4

A-E : A-D-E:10

A-F : A-D-E-F:22

A B C D E F
0 6 11 4 10 22

对比现在的最短距离和上一个数组的距离,到相同节点取最小的,更新C点走A-D-E-C 为11,比之前的A-D-B-C14距离更近,更新到F点距离,得到如下数组:

A B C D E F
0 6 11 4 10 22

此时E点加入最短路径范围中。

第四次选取

A B C D E F
0 6 11 4 10 22

第一步:选取除了A B D E节点之外的剩余节点中最短节点,为点C

第二步:以C点为最新节点,更新最短路径数组

A-A : 0

A-B : A-D-B:6

A-C : A-D-E-C:11

A-D : 4

A-E : A-D-E:10

A-F : A-D-E-C-F:16

A B C D E F
0 6 11 4 10 16

对比现在的最短距离和上一个数组的距离,到相同节点取最小的,更新到F点距离,可以得到如下数组:

A B C D E F
0 6 11 4 10 16

第五次选取

A B C D E F
0 6 11 4 10 16

第一步:选取除了A B C D E节点之外的剩余节点中最短节点,也就是最后一个节点:F

第二步:以F点为最新节点,更新最短路径数组。由于F点是最后一个点,所以也不用更新数组,目前的数组就是所求数组

将F点加入最短路径范围中,此时所有的点都加入了最短路径范围,也就是说A点到所有点的距离都找到了。最总得出的距离值为:

最终得到的结果为:

A B C D E F
0 6 11 4 10 16

最终结果

相应的A点到所有点的最短路径走法最终得到的结果为:

A B C D E F
0 6 11 4 10 16

A-A:0

A-B : A-D-B:6

A-C : A-D-E-C:11

A-D:4

A-E:A-D-E:10

A-F:A-D-E-C-F:16

算法总结

Dijkstra算法作为求最短路径的经典算法,个人理解为算法提供了一种思想,每走一步都是找到最短的路径,并且每走一步都实时更新所有距离,保证每次都选择最短路径。

python实现Dijkstra

将以上的过程使用python来实现。

首先总结一个Dijkstra算法的核心思想,分成两步走:

  1. 构造一个最短路径数组,每次找到数组中未访问的节点里最小的点
  2. 以上一步的节点为最新节点,更新起始点到所有点的距离

使用python就是实现这两步即可

数据准备

二维矩阵

如何描述一个图呢?通常有两种方式,分别是:十字链表和二维矩阵。因为二维矩阵更加直观,所以选择二维矩阵。

将上面的图描述成一个二维矩阵



无穷大使用MAX = float('inf')表示,该数值是python中表示无穷大的一个值。



这个二维矩阵真正直观之处在哪里呢?是能够看到任意一个点到其他点的距离。如想看D点到其他点的距离,就是:



在我们的算法两步走中第二步要更新A点经过某点到其他点的距离,正是使用了这个特征。

MAX= float('inf')

matrix = [
[0,10,MAX,4,MAX,MAX],
[10,0,8,2,6,MAX],
[MAX,8,10,15,1,5],
[4,2,15,0,6,MAX],
[MAX,6,1,6,0,12],
[MAX,MAX,5,MAX,12,0]
]

最短路径数组

在上面讲解算法过程中有一个重要的的最短路径数组,不断更新该数组直到所有的点都被访问到。使用python语言,构造该数组:

distance = [MAX] * len(matrix)

len(matrix) 实际上算出的图的点的个数。初始化时所有的节点都是不可达。

在算法过程中还有一个重要的数组,并没有体现出来,但是在python计算时也很重要,那就是访问过的点。每一次访问之后就要将访问过的点加入到该数组中,这样做是为了避免重复访问。

used_node = [False] * len(matrix)

初始化时认为所有点都没有访问到

代码实现



MAX= float('inf')

matrix = [
[0,10,MAX,4,MAX,MAX],
[10,0,8,2,6,MAX],
[MAX,8,10,15,1,5],
[4,2,15,0,6,MAX],
[MAX,6,1,6,0,12],
[MAX,MAX,5,MAX,12,0]
] def dijkstra(matrix, start_node): #矩阵一维数组的长度,即节点的个数
matrix_length = len(matrix) #访问过的节点数组
used_node = [False] * matrix_length #最短路径距离数组
distance = [MAX] * matrix_length #初始化,将起始节点的最短路径修改成0
distance[start_node] = 0 #将访问节点中未访问的个数作为循环值,其实也可以用个点长度代替。
while used_node.count(False):
min_value = float('inf')
min_value_index = 999 #在最短路径节点中找到最小值,已经访问过的不在参与循环。
#得到最小值下标,每循环一次肯定有一个最小值
for index in range(matrix_length):
if not used_node[index] and distance[index] < min_value:
min_value = distance[index]
min_value_index = index #将访问节点数组对应的值修改成True,标志其已经访问过了
used_node[min_value_index] = True #更新distance数组。
#以B点为例:distance[x] 起始点达到B点的距离,
#distance[min_value_index] + matrix[min_value_index][index] 是起始点经过某点达到B点的距离,比较两个值,取较小的那个。
for index in range(matrix_length):
distance[index] = min(distance[index], distance[min_value_index] + matrix[min_value_index][index]) return distance start_node = int(input('请输入起始节点:'))
result = dijkstra(matrix,start_node)
print('起始节点到其他点距离:%s' % result)

结果:

请输入起始节点:0
起始节点到其他点距离:[0, 6, 11, 4, 10, 16]

简单总结

学习python实现Dijkstra重要的地方有几点:

  1. 数据构造 二维矩阵表示图
  2. 图的访问方式 更新最短路径数组的过程无非就是分别比较二维矩阵数组中某一行的值和最短路径数组的值

熟悉这样的处理方式,再有类似的算法也能找到解决的思路。例如一个二维矩阵,从起始点开始只能走向下的相邻的元素,求达到某点的最短路径。

希望通过该篇文章,能够深刻理解Dijkstra算法,做到心中有数,手中有活。如果觉得写的清晰,帮忙点亮大拇指!

一篇文章讲透Dijkstra最短路径算法的更多相关文章

  1. Java邻接表表示加权有向图,附dijkstra最短路径算法

    从A到B,有多条路线,要找出最短路线,应该用哪种数据结构来存储这些数据. 这不是显然的考查图论的相关知识了么, 1.图的两种表示方式: 邻接矩阵:二维数组搞定. 邻接表:Map<Vertext, ...

  2. 练习 Dijkstra 最短路径算法。

    练习 Dijkstra 最短路径算法. #coding: utf-8 # Author: woodfox, Oct 14, 2014 # http://en.wikipedia.org/wiki/Di ...

  3. 数据结构(c++)(第二版) Dijkstra最短路径算法 教学示范代码出现重大问题!

    前言 去年在数据结构(c++)的Dijkstra教学算法案例中,发现了一个 bug 导致算法不能正常的运行,出错代码只是4行的for循环迭代代码. 看到那里就觉得有问题,但书中只给了关键代码的部分,其 ...

  4. Dijkstra最短路径算法[贪心]

    Dijkstra算法的标记和结构与prim算法的用法十分相似.它们两者都会从余下顶点的优先队列中选择下一个顶点来构造一颗扩展树.但千万不要把它们混淆了.它们解决的是不同的问题,因此,所操作的优先级也是 ...

  5. Python 图_系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径算法

    1. 前言 因无向.无加权图的任意顶点之间的最短路径由顶点之间的边数决定,可以直接使用原始定义的广度优先搜索算法查找. 但是,无论是有向.还是无向,只要是加权图,最短路径长度的定义是:起点到终点之间所 ...

  6. 一篇文章讲明白vue3的script setup,拥抱组合式API!

    引言 vue3除了Composition API是一个亮点之外,尤大大又给我们带来了一个全新的玩意 -- script setup,对于setup大家相信都不陌生,而对于script setup有些同 ...

  7. Dijkstra 最短路径算法 秒懂详解

    想必大家一定会Floyd了吧,Floyd只要暴力的三个for就可以出来,代码好背,也好理解,但缺点就是时间复杂度高是O(n³). 于是今天就给大家带来一种时间复杂度是O(n²),的算法:Dijkstr ...

  8. Dijkstra最短路径算法实例

    #include <stdio.h>#include <stdlib.h>/* Dijkstra算法 */#define VNUM 5#define MV 65536int P ...

  9. 关于Dijkstra最短路径算法

    Dijkstra算法,不是很明白,今天找了一些博客看了一下,决定自己也写一个为以后忘记的时候可以看做准备. 实际上,如果理解没错的话,该算法实际上和枚举法有点像,只不过,在选取出发路径的路径都是最短路 ...

随机推荐

  1. mysql优化–explain分析sql语句执行效率

    Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优 ...

  2. Synchronized 和 ReentrantLock (Lock )的区别

    原始构成 Synchronized 是关键字,属于JVM层面,底层是通过 monitorenter 和 monitorexit 完成,依赖于 monitor 对象来完成.由于 wait/notify ...

  3. 用Visual Studio2019自定义项目模板

    项目模板简介 众所周知,在我们使用VS新建项目时,都需要选择一个项目模板,如下图: 我们选择完项目模板进行创建,创建完成之后,可以发现项目中已经包含了一些基础的文件.例如MVC: 可以看到,MVC项目 ...

  4. 谈谈Spring bean的生命周期(一)

    简介 本片文章主要讲Spring IOC容器中 bean 的生命周期 Spring bean 生命周期 Spring 中bean的声明周期 可以分为如下4个阶段: 实例化阶段--Instantiati ...

  5. JVM 运行时数据区(二)

    @ 目录 运行时数据区 共享区 堆区 方法区 隔离区 虚拟机栈 栈帧 本地方法栈 程序计数器 运行时数据区 JVM 运行时数据区主要分为5块 方法区 JDK1.8以后叫做元数据区(Metaspace) ...

  6. Qt子窗口设置背景色只能应用到其中的部件的问题

    问题描述:设置父窗口后子窗口会嵌在父窗口中,背景变透明,此时用qss设置子窗口的背景色发现只应用到的子窗口的控件中,除控件外的地方并没有应用到背景色. 解决方法:不使用qss设置背景色,重写paint ...

  7. An SWT error has occurred

    对话框标题:Problem Occurred 对话框内容:Unhandled event loop exception No more handles 对话框按钮:第一个超链接:Show Error ...

  8. Rx-Volley 自己来封装

    自从15年接触了RxJava,对函数式编程越发的喜爱.以前Android项目上网络层都是统一的使用Volley,已经对网络请求的回调,多个回调嵌入各种不爽了,趁着年前任务轻松,赶紧的将Volley封装 ...

  9. java调用oracle存储过程返回多条结果集

    oracle版本:11g oracle存储过程,使用游标的方式返回多行.多列数据集合: CREATE OR REPLACE PROCEDURE SP_DATA_TEST( /*P_ID IN INT, ...

  10. Loadrunner中遇到Failed to connect to server[10061] connection refused错误

    (1)run-time setting/browser emulation中,将simulate a new user on each iteration  选项去掉(默认是选中的). 重新运行一切正 ...