Dijkstra求最短路篇一(全网最详细讲解两种方法,适合小白)(python,其他语言也适用)

前言:

Dijkstra算法博客讲解分为两篇讲解,这两篇博客对所有有难点的问题都会讲解,小白也能很好理解。看完这两篇博客后保证收获满满。

本篇博客讲解朴素Dijkstra算法,第二篇博客讲解堆优化Dijkstra算法Dijkstra求最短路篇二(全网最详细讲解两种方法,适合小白)(python,其他语言也适用),两中算法思路大体相同,但时间复杂度有所区别。

  • 朴素Dijkstra算法:时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 堆优化Dijkstra算法:时间复杂度 O ( m l o g n ) O(mlogn) O(mlogn)

两篇博客给出的题目内容一样,只有数据规模不一样。

题目:

题目链接:
849. Dijkstra求最短路 I

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为非负值。请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。

数据范围(两题不同处)
1 ≤ n ≤ 500 1≤n≤500 1n500,
1 ≤ m ≤ 1 0 5 1≤m≤10^5 1m105

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

Dijkstra算法介绍:

Dijkstra求最短路问题是图论的经典问题,首先介绍一下Dijkstra算法的流程图:

迪杰斯特拉算法采用的是一种贪心的策略。

求源点到其余各点的最短距离步骤如下:

  1. 用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。初始时,dist数组的各个元素为无穷大。
    用一个状态数组 state 记录是否找到了源点到该节点的最短距离,state[i] 如果为真,则表示找到了源点到节点 i 的最短距离,state[i] 如果为假,则表示源点到节点 i 的最短距离还没有找到。初始时,state 各个元素为假。

在这里插入图片描述

  1. 源点到源点的距离为 0。即dist[1] = 0。在这里插入图片描述
  2. 遍历 dist 数组,找到一个节点,这个节点是:没有确定最短路径的节点中距离源点最近的点。假设该节点编号为 i。此时就找到了源点到该节点的最短距离,state[i] 置为 1。
    在这里插入图片描述
  3. 遍历 i 所有可以到达的节点 j,如果 dist[j] 大于 dist[i] 加上 i -> j 的距离,即 dist[j] > dist[i] + w[i][j](w[i][j] 为 i -> j 的距离) ,则更新 dist[j] = dist[i] + w[i][j]。在这里插入图片描述
  4. 重复 3 4 步骤,直到所有节点的状态都被置为 1。在这里插入图片描述
  5. 此时 dist 数组中,就保存了源点到其余各个节点的最短距离。
    在这里插入图片描述

思路:

首先对数据进行存储,图的存储有两种方式,一种是邻接表,一种是邻接矩阵。题目中的数据规模用邻接矩阵存储(本题数据规模是稠密图,更省空间)。

为什么要用邻接矩阵去存贮,而不是邻接表?

我们采用邻接矩阵还是采用邻接表来表示图,需要判断一个图是稀疏图还是稠密图。稠密图指的是边的条数|E|接近于|V|²,稀疏图是指边的条数|E|远小于于|V|²(数量级差很多)。本题是稠密图,显然稠密图用邻接矩阵存储比较节省空间,反之用邻接表存储。

这里需要注意的是,题目中指出图中可能存在重边和自环。

  • 重环是指两个点之间有多条边,每个边的距离不同。
  • 自环是指一个点存在一条连向自己的边。

所以为了解决重环和自环问题,存储过程中需要存距离的最小值

邻接矩阵存储代码如下:

n, m = map(int, input().split()) # 图的节点个数和边数
#  构建邻接矩阵 float("inf")在python中是无限大的意思
num = [[float("inf") for j in range(n + 1)] for i in range(n + 1)] 
for i in range(m):a, b, c = map(int, input().split())num[a][b] = min(num[a][b], c)

以题目实例为例,打印num数组(这里存储的是有向边,所以不是关于对角线对称的):

[[inf, inf, inf, inf], [inf, inf, 2, 4], [inf, inf, inf, 1], [inf, inf, inf, inf]]

邻接矩阵构建完成之后就要进行Dijkstra算法,这里直接给出代码,用详细代码给大家进行讲解。

代码及详细注释:

n, m = map(int, input().split()) # 图的节点个数和边数
#  构建邻接矩阵 float("inf")在python中是无限大的意思
num = [[float("inf") for j in range(n + 1)] for i in range(n + 1)] 
for i in range(m):a, b, c = map(int, input().split())num[a][b] = min(num[a][b], c)
dist = [float('inf') for _ in range(n + 1)]  # dist 数组用于记录每一个点距离第一个点的距离
dist[1] = 0 # 源点到源点的距离为置为 0
state = [False for _ in range(n + 1)]  # state 用于记录该点的最短距离是否已经确定def dijkstra():for i in range(n):  # 有n个点所以要进行n次迭代 (这里迭代多了并不影响结果,因为有state数组来记录每个点的状态)t = -1  # 该点不唯一,只要是在n个节点之外或者是最后一个节点就行(也可以是0,-1表示最后一个节点)for j in range(1, n + 1): # 循环每个点,找到最短距离的点,并把它赋值给t,(j表示各个节点)if not state[j] and dist[j] < dist[t]:  # 该点未找到最短距离,且j点到第一个点的距离小于t点到第一个点的距离t = jstate[t] = True   # 标记该点距离确定for j in range(1, n + 1):  #  # 根据该点更新其他所有点的距离dist[j] = min(dist[j], dist[t] + num[t][j])# 判断最后一个点的最短距离是否找到,如果为无穷大,则表示未找到,返回-1,否则返回最短距离dist[-1]if dist[n] == float('inf'):return -1else:return dist[-1]print(dijkstra())

代码看着稍微复杂点,但拿实例数据跟着过一遍就一目了然。

这里拿实例带大家过一遍:

  1. 首先进行迭代,给t赋值为最后一个点(点3)的索引(索引值为-1,写n也可以)。之后循环这三个点,只有点1的dist距离(0)是小于最后一个点的dist值的(无穷大),所以 state[1] = True,然后循环所有的点,更新所有点到源点(点1始终是源点)的距离,dist[2] = 2, dist[3] = 4(点1与点2和点3直接相连,直接更新当前距离即可)
  2. 然后给t重新赋值为-1(这点很关键,不然之后都是跟点1进行比较了,算不出结果)。循环所有点后发现只有点2dist距离(2)是小于最后一个点的dist距离的(4)(点1的state为True,也就是已经找到最短距离了,不参与其中),之后都是重复第一步操作,更新dist[3] = 3
  3. 再一次循环时未更新值,只是把state[3] = True 的最短距离更新了,之后就会输出结果3

下面也会对大家难以理解的问题进行讲解回答(重点!!!):

  1. t为什么要赋值为 -1

答:由于每一次都要找到还没有确定最短路距离的所有点中,距离当前的点最短的点(也就是始终为无穷大的值的点)。t = - 1是为了在st这个集合中找第一个点更新时候的方便所设定的(也可以是0,因为题目中不存在点0,但0的索引确始终存d在)。

  1. 为什么要进行n次迭代,dijkstra函数中for i in range(n):的意义是什么?

答:这里的每次迭代主要是更新state数组的最短距离是否被找到,每一次迭代都会固定一个点的最短距离。

  1. 如果是问编号a到b的最短距离该怎么改呢?

初始化时 dist[a]=0, 以及返回时return dist[b]

  1. 为什么dist要初始化为无穷,邻接矩阵的部分点用无穷表示呢?

答:首先对于邻接矩阵而言,存储的是两点之间边的距离,如果两点之间不存在边,那肯定用无穷大来表示到达不了,dist数组初始化无穷大是因为这里要求最短路径,需要初始化一个较大值,这里初始化无穷大以免出错。

总结:

朴素版dijkstra适合稠密图

思路:

集合S为已经确定最短路径的点集。

  1. 初始化距离 一号结点的距离为零,其他结点的距离设为无穷大(看具体的题)。
  2. 循环n次,每一次将集合S之外距离最短X的点加入到S中去(这里的距离最短指的是距离1号点最近。 点X的路径一定最短,基于贪心,严格证明待看)。然后用点X更新X邻接点的距离。

时间复杂度分析:

  • 寻找路径最短的点: O ( n 2 ) O(n^2) O(n2)
  • 加入集合S: O ( n ) O(n) O(n)
  • 更新距离: O ( m ) O(m) O(m)
  • 所以总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

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

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

相关文章

从C++示例理解开闭原则

开闭原则要求我们在编写代码时&#xff0c;尽量不去修改原先的代码&#xff0c;当出现新的业务需求时&#xff0c;应该通过增加新代码的形式扩展业务而不是对原代码进行修改。 假如我们现在有一批产品&#xff0c;每个产品都具有颜色和大小&#xff0c;产品其定义如下&#xf…

父子进程概述

父子进程概述 总结了两篇博客&#xff0c;对父子进程涉及的问题进行了简要总结&#xff08;参考博客在文章末尾&#xff09; 创建进程的目的一般有两个&#xff1a; 一是父进程希望生成一份自己的副本&#xff0c;执行同一个程序中不同的代码片段。二是让子进程执行不同的程序…

python with 和 上下文管理器

with with操作写法简单又安全 文件操作使用with会自动调用关闭文件操作&#xff0c;即使出现异常也会自动调用文件关闭操作 上下文管理器 with语句强大的根本是由上下文管理器支持的 通过open打开的的文件&#xff0c;赋值给的一个变量file&#xff0c;file就是文件对象&am…

linux docker常用命令记录

一、防火墙 1. 开启防火墙 systemctl start firewalld 2.查看防火墙状态 systemctl status firewalld 二、docker 1.启动docker systemctl start docker 2.关闭docker systemctl stop docker 3.重启docker systemctl restart docker4.查看docker 运行状态 systemc…

Kotlin 函数

文章目录 函数的定义函数的返回值参数默认值 & 调用时参数指定函数作用域Lambda 表达式匿名函数内联函数扩展函数中缀函数递归函数 & 尾递归函数 函数的定义 函数可以理解成一个小小的加工厂&#xff0c;给入特定的原材料&#xff0c;它就会给出特定的产品。 fun [接…

知识运维概述

文章目录 知识运维研究现状技术发展趋势 知识运维 由于构建全量的行业知识图谱成本很高&#xff0c;在真实的场景落地过程中&#xff0c;一般遵循小步快走、快速迭代的原则进行知识图谱的构建和逐步演化。知识运维是指在知识图谱初次构建完成之后&#xff0c;根据用户的使用反馈…

小白跟做江科大32单片机之对射式红外传感器计次

原理部分 1中断示意图&#xff0c;中断会打断主函数的执行&#xff0c;终端执行完成之后再返回主函数继续执行 2.STM32中断 这些灰色的是内核中断 这些白色的是普通中断 3.NVIC统一管理中断&#xff0c;每个中断通道都拥有16个可编程的优先等级&#xff0c;可对优先级进行分组…

独孤思维:10个T的赚钱资料,要不要

01 今天有一个通过网站引流过来的粉丝。 问我&#xff0c;为啥网站不设置付费&#xff0c;这样直接转化成网站vip。 我说&#xff0c;我想把用户沉淀到私域。 其实这个问题&#xff0c;独孤在早年做网站的时候也思考过。 前端给资料&#xff0c;是为了后端引流加个人号&am…

java-this关键字

Java 中的 this 关键字是一个特殊的引用&#xff0c;它代表当前对象。在 Java 中&#xff0c;this 关键字可以在类的构造函数、方法、块和初始化语句中使用。this 关键字的主要作用是&#xff1a; 1. 引用当前对象的属性&#xff08;Field&#xff09;&#xff1a;使用 this 关…

OCP题库

Q2.分析下面的语句和输出: mysql> SHOW GRANTS FOR jsmith; ------------------------------------------------------------------------------------------------------------------- IGrants for jsmith% | -----------------------------------------------------------…

Redis缓存(笔记一:缓存介绍和数据库启动)

目录 1、NoSQL数据库简介 2、Redis介绍 3、Redis(win系统、linux系统中操作) 3.1 win版本Redis启动 3.2 linux版本Redis启动 1、NoSQL数据库简介 技术的分类&#xff1a;&#xff08;发展史&#xff09; 1、解决功能性的问题&#xff1a;Java、Jsp、RDBMS、Tomcat、HTML、…

Filter和ServletContext和Listener

目录 Filter案例 解决全站乱码问题 登录权限校验 ServletContext对象 Listener&#xff08;监听器&#xff09; Filter案例 解决全站乱码问题 我们每次访问每个servlet都要书写处理请求和响应乱码的代码&#xff0c;这样代码十分冗余&#xff0c;所以我们可以在过滤中 We…

Java——变量

一、变量介绍 变量就是申请内存来存储值。也就是说&#xff0c;当创建变量的时候&#xff0c;需要在内存中申请空间。内存管理系统根据变量的类型为变量分配存储空间&#xff0c;分配的空间只能用来储存该类型数据。 1、变量声明和初始化 变量的声明&#xff1a; int a; i…

44-1 waf绕过 - WAF的分类

一、云 WAF 通常包含在 CDN 中的 WAF。在配置云 WAF 时&#xff0c;DNS 需要解析到 CDN 的 IP 上。请求 URL 时&#xff0c;数据包会先经过云 WAF 进行检测&#xff0c;如果通过检测&#xff0c;再将数据包流向主机。 二、硬件IPS/IDS防护、硬件WAF 硬件IPS/IDS防护&#xff…

VS Code 开发小技巧

VS Code的开发小技巧 添加代码片段 平时开发的时候&#xff0c;可以快速创建一个空白的模板。 一个快速生成代码片段的网站&#xff1a;https://snippet-generator.app/ 打开网站&#xff0c;把常用的模板代码复制进去&#xff0c;就会自动生成VS Code可以使用的代码片段了。…

最低要求条件下的商环定义

从一篇老外的书籍看到的&#xff0c;感觉挺不错&#xff0c;记录下&#xff01;&#xff01;&#xff01; 【商环定义】&#xff08;最低要求&#xff09; 设 R ≠ { 0 } R \neq \left\{ 0 \right\} R{0}为交换幺环&#xff0c;设子集 S ⊆ R S \subseteq R S⊆R满足乘法运…

从零到一建设数据中台 - 关键技术汇总

一、数据中台关键技术汇总 语言框架&#xff1a;Java、Maven、Spring Boot 数据分布式采集&#xff1a;Flume、Sqoop、kettle 数据分布式存储&#xff1a;Hadoop HDFS 离线批处理计算&#xff1a;MapReduce、Spark、Flink 实时流式计算&#xff1a;Storm/Spark Streaming、…

没有 rr 头的 kamailio 路由脚本

分享下笔者最近编写的 kamailio 路由脚本 不用 rr 模块&#xff0c;因为有些 sip 协议栈不支持 rr 头处理 sip 注册直接回 200 OK&#xff0c;这部分目前不是重点更换 contact 头&#xff0c;换成 kamailio 自己目前只支持 sip transport 为 udp&#xff0c;以后可能支持 tcp&…

2024.05.30更新票星球抢购软件

文章目录 软件功能订阅须知早期代码软件功能 自持自定义搜索演唱会信息支持添加、删除观影人信息支持多账号并发抢票支持捡漏模式支持IP代理订阅须知 订阅后如果有问题,请联系博主,如果不懂可以免费提供讲解和远程服务早期代码 def enter_concert(self):print(u###打开浏览器…

Unity DOTS技术(一)简介

文章目录 一.概述二.将会介绍的内容三.DOTS技术与传统方式的不同传统问题DOTS技术 四.插件安装 一.概述 传统的游戏开发中,如果有成千上万的物体在场景中运动,那么你一定会认为是疯了.但有了Dost技术这一些都将变成可能.如图场景中有10000个物体在同时运动,帧率即能保持在60Fp…