最短路之Dijkstra(15张图解)

news2024/11/24 8:37:01

🌼多年后再见你 - 乔洋/周林枫 - 单曲 - 网易云音乐 闲来无事听听歌

Dijkstra可解决“单源最短路径”问题 

四种最短路算法

Floyd算法

时间复杂度高,但实现容易(5行核心代码),解决负权,适用于数据范围小

Dijkstra算法

不能解决负权边,但具有良好扩展性,且复杂度较低

Bellman-Ford / 队列优化Bellman-Ford

可解决负权边,且复杂度较低

本节学习指定一个点(源点)到其他顶点的最短路径(单源最短路径

比如下图,求1号顶点到2,3,4,5,6号顶点的最短路径  

与Floyd-Warshall一样,我们依然采用二维数组e存储顶点和边的关系,初始值如下图:

还需要一个一维数组dis(tant)来存储1号顶点到其他顶点的初始路程,如下图:

我们将此时dis数组中的值称为最短路程的“估计值” 

第一步:找确定值 (1)

先找离源点(1号)最近的顶点,由dis数组可知,2号最近,选择2号顶点后,dis[2]的值就从“估计值”变成了“确定值”(表示1号到2号的最短路程已确定)

敲重点!

下面的推理是Dijkstra算法的核心, ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

为什么确定了呢?因为:

1,离源点(1号顶点)最近的是2号顶点

2,这个图所有边都是正数

所以,通过其他顶点中转,使1号先经其他顶点,再到2号顶点的路程,肯定更长

所以此时dis[2]就是1号到2号最短路程的确定值

↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 

第二步:从刚被确定的顶点“出边”

第一步确定了一个确定值,第二步我们从这个被确定的点,进行“出边”,有2->3, 2->4两条边

先判断 dis[2] + e[2][3] < dis[3],即判断能否借2号中转,使1号到3号的路程缩短,由下图可知,dis[2] + e[2][3] == 1 + 9等于10,dis[3] == 12,可以缩短,所以dis[3]更新为10

这个过程有个专业术语叫“松弛” ,即1号顶点到3号顶点的路程dis[3],通过2->3这条边“松弛”成功,这便是Dijkstra算法的主要思想

通过“边”来松弛1号顶点到其他顶点的路程

同理,判断dis[2] + e[2][4] < dis[4],dis[2] + e[2][4] == 1 + 3等于4,dis[4] == ∞

4 < ∞,所以dis[4]更新为4

 

围绕确定点2的出边结束,开始下一轮找确定点

第一步:找确定值 (2)

剩下未确定的3,4,5,6号顶点,选出离1号最近的点,通过上面更新后的dis数组,当前最近的是4号顶点,所以dis[4]从估计值变成确定值 

为什么此时dis[4]就确定是1号到4号的最短路径了呢?因为dis数组更新后,假设存在经一个或几个点中转,路程比dis[4]小,首先经2号中转后离1号顶点最近的点已确定,就是4号,所以不存在经2号中转比dis[4]小的

至于经3,5,6中转比dis[4]小,更不存在了,此时1到3,5,6不经中转都比dis[4]大

所以更新后的dis[4]必然确定了

第二步:从刚被确定的顶点“出边”

从被确定的4号顶点出边,4->3, 4->5, 4->6,

dis[4] + e[4][3] < dis[3](8 < 10),dis[4] + e[4][5] < dis[5](17 < ∞),dis[4] + e[4][6] < dis[6](19<∞)

出边完毕后,看下图:

第一步:找确定值 (3)

再在剩下未确定的3, 5, 6中,找出离1号最近的,即dis中未确定中的最小值,8 < 17 < 19

所以dis[3]从估计值变为确定值 

第二步:从刚被确定的顶点“出边”

只能从3向5出边,3不和6直接相连(不能出边),只能3->5了,dis[3] + e[3][5] == 8 + 5 == 13

dis[5] == 17,  13 < 17,所以dis[5]更新为13

第一步:找确定值 (4)

从剩下的5,6号找离1号最近的顶点,是5号,dis[5]从估计值变成确定值

第二步:从刚被确定的顶点“出边”

dis[5] + e[5][6] < dis[6](17 < 19),dis[6]更新为17

第一步:找确定值 (5)

在剩下的6号中找离1号最近的,就他一个了,所以dis[6]从估计值变确定值 

第二步:从刚被确定的顶点“出边”

没有未确定的值,出边失败~   算法结束

最终 

这便是1号顶点到其他顶点的最短路径,至此,“单源最短路径”问题解决

思路 

每次找到离源点最近的点,以该点为中心对未确定的点进行扩展 

1, 

将所有顶点分为两部分,一部分已确定(确定值),一部分未确定(估计值)

已确定的顶点,用book数组标记为1,比如book[4] = 1 

2, 

将某一点到源点最短路径用dis数组保存;二维数组e中,i 到 j 无法到达用e[i][j] = ∞来表示,i 到 i 用e[i][i] = 0来表示 

完整代码 

#include<cstdio>
int main()
{
    int e[10][10], dis[10], book[10], i, j, n, m;
    int t1, t2, t3, u, v, Min;
    int inf = 1e8; //infinity(n.)无穷

    //读入n个顶点, m条边
    scanf("%d%d", &n, &m);

    //初始化
    for(i = 1; i <= n; ++i)
        for(j = 1; j <= n; ++j) {
            if(i == j) e[i][j] = 0;
            else e[i][j] = inf;
        }

    //读入边
    for(i = 1; i <= m; ++i) {
        scanf("%d%d%d", &t1, &t2, &t3);
        e[t1][t2] = t3;
    }

    //初始化dis数组, 表示源点1号到其他点初始路程
    for(i = 1; i <= n; ++i)
        dis[i] = e[1][i];

    //初始化book数组
    for(i = 1; i <= n; ++i)
        book[i] = 0;

    //Dijkstra算法核心
    //源点不用确定, 所以是n - 1次遍历
    for(i = 1; i <= n - 1; ++i) {
        Min = inf;
        for(j = 2; j <= n; ++j) { //从顶点2开始
            //找确定值(未确定中找最小值)
            if(book[j] == 0 && dis[j] < Min) {
                Min = dis[j];
                u = j;
            }
        }
        book[u] = 1; //顶点u已确定
        //从刚被确定的顶点出边
        for(v = 2; v <= n; ++v) //从顶点2开始
            if(e[u][v] < inf && dis[u] + e[u][v] < dis[v])
            //两点连通且可更新
                dis[v] = dis[u] +e[u][v];
    }
    for(int i = 1; i <= n; ++i)
        printf("%d ", dis[i]);

    return 0;
}

输入输出

6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
0 1 8 4 13 17

由上述代码,可知Dijkstra时间复杂度为O(n^2) 

每次找到离源点最近顶点的时间复杂度是O(n),这里我们可以用优化(下下个博客讲) 

使找最近顶点的复杂度从O(n) --> O(logn)

        另外对于边数m小于n^2的稀疏图来说(我们称m < n^2的图为稀疏图,m > n^2的图为稠密图)

        可以用邻接表来代替矩阵,使整个算法时间复杂度优化O((m + n) * logn),但稀疏图的最坏情况是m == n^2,此时 (m + n) * logn 比 n^2 还大

        当然大多数情况不会有那么多边 

下面这个是稠密图

 

总结

每次出边就要判断,判断dis[a] + e[a][c] < dis[c]成立,就要更新dis[c] = dis[a] + e[a][c]

源点 -> a -> c的路程  小于  源点 -> c的路程 ,其中dis[a]是确定值

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

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

相关文章

凭借这份《2022测试八股文》候选者逆袭面试官,offer拿到手软

《2023测试面试八股文》800 道软件测试面试真题&#xff0c;高清打印版打包带走&#xff0c;横扫软件测试面试高频问题&#xff0c;涵盖测试理论、Linux、MySQL、Web 测试、接口测试、App 测试、Python、Selenium、性能测试、LordRunner、计算机网络、数据结构与算法、逻辑思维…

opencv——傅里叶变换、低通与高通滤波及直方图等操作

1、傅里叶变换a、傅里叶变换原理时域分析&#xff1a;以时间为参照进行分析。频域分析&#xff1a;相当于上帝视角一样&#xff0c;看事物层次更高&#xff0c;时域的运动在频域来看就是静止的。eg&#xff1a;投球——时域分析&#xff1a;第1分钟投了3分&#xff0c;第2分钟投…

“学好英语网”首页制作

“学好英语网”首页制作一、实验名称&#xff1a;二、实验日期&#xff1a;三、实验目的&#xff1a;四、实验内容&#xff1a;五、实验步骤&#xff1a;六、实验结果&#xff1a;七、源程序&#xff1a;八、心得体会&#xff1a;一、实验名称&#xff1a; “学好英语网”首页…

Linux第三讲

目录 三、 磁盘和文件管理和使用检测和维护 3.1 磁盘目录 3.2 安装软件 3.2.1 rpm命令 3.2.2 克隆虚拟机 3.2.3 yum或压缩包方式安装jdk 3.2.4 使用虚拟机运行SpringBoot项目 3.2.5 安装mysql80&#xff08;57&#xff09; 3.2.6 运行web项目 3.2.7 安装tomcat 三、 …

情人节前夕,竞品在小红书平台如何布局营销策略?

情人节作为全球性消费型节日之一&#xff0c;其营销价值不言而喻。以女性用户群体为主导的小红书平台&#xff0c;更是成为该营销节点众多品牌争夺流量的阵地。 那么&#xff0c;情人节前夕竞品在小红书平台布局什么样的营销策略&#xff1f;创作何种内容&#xff0c;如何推广&…

手把手教你用Python做可视化数据,还能调节动画丝滑度

数据可视化动画还在用Excel做&#xff1f; 现在一个简单的Python包就能分分钟搞定&#xff01; 而且生成的动画也足够丝滑&#xff0c;效果是酱紫的&#xff1a; 这是一位专攻Python语言的程序员开发的安装包&#xff0c;名叫Pynimate。 目前可以直接通过PyPI安装使用。 使用…

线程池小结

什么是线程池 线程池其实就是一种多线程处理形式&#xff0c;处理过程中可以将任务添加到队列中&#xff0c;然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象; 为什么使用线程池 …

1001. x+y 1002. x+y+z etiger.vip 解析与答案

目录 1001题 题目描述 输入输出格式 输入格式 输出格式 输入输出样例 输入样例#1: 输出样例#1: 头文件和数组等初始定义 第一个函数——converts 第二个函数——add 第三个函数——print 主函数部分 完整代码 1002题 题目描述 输入输出格式 输入格式 输出格…

Vue3快速入门【一】

Vue3快速入门一、Vue脚手架1.1、Vite简介1.2、创建项目二、更换Vue模板支持工具三、项目相关命令解析四、生命周期钩子函数五、ref方法的几种使用方式5.1、ref方法(操作基本类型数据)5.2、ref方法(操作复杂类型数据)5.3、ref方法获取标签六、reacttive方法和toRefs方法七、setu…

开发人员 ONLYOFFICE 文档 v7.3:API 与文档生成器更新

随着版本 7.3 新功能的发布&#xff0c;我们也对编辑器、插件以及文档生成器的 API 进行了更新。在下方了解更多详情。 增强的 WOPI 支持 从现在开始&#xff0c;您可在 WOPI 集成中使用二进制格式&#xff0c;如 doc、ppt 以及 xls。我们现已实现了相应方案&#xff0c;其中包…

ClickHouse 合并树表引擎 MergeTree 索引与数据存储方式

目录 1. 一级索引 1.1 稀疏索引 1.2 索引粒度 1.3 索引数据的生成规则 1.4 索引的查询过程 2. 二级索引 2.1 granularity 与 index_granularity 2.2 跳数索引的生成规则

阿里6面,成功唬住面试官拿了27K,软件测试面试也没有传说中那么难吧....

阿里的面试挺独特&#xff0c;每轮面试都没有 HR 约时间&#xff0c;一般是晚上 8 点左右面试官来一个电话&#xff0c;问是否能面试&#xff0c;能的话开始面&#xff0c;不能就约一个其它时间。 全程 6 面&#xff0c;前五面技术面&#xff0c;电话面试&#xff0c;最后一面…

KEITHLEY吉时利2410数字源表

产品概览 Keithley 2410 高压源表专为需要紧密耦合源和测量的测试应用而设计。Keithly 2410 提供精密电压和电流源以及测量功能。它既是高度稳定的直流电源&#xff0c;又是真正的仪器级 5-1/2 数字万用表。电源特性包括低噪声、精度和回读。万用表功能包括高重复性和低噪声。…

《真象还原》读书笔记——第三章 完善MBR(3.5 硬盘)

3.5 硬盘介绍 3.5.2 硬盘工作原理 柱面-磁头-扇区 磁道的编号从0 开始&#xff0c;相同编号的此道组成的管状区域就是柱面。 盘面和磁头一一对应&#xff0c;所以用磁头号表示盘面&#xff0c;磁头编号从上到下从0开始。 扇区编号与盘面和磁道不同&#xff0c;各磁道内的扇区都…

选择万德L2接口需要遵循什么原则?

万德L2接口是一种可以让程序拥有查询股票相关数据的应用查询编程接口&#xff0c;通过这个数据接口&#xff0c;可以直接调用相应的数据&#xff0c;而不用进行额外的编程工作&#xff0c;甚至不需要理解其中的工作机制&#xff0c;是十分方便快捷的一种数据接口。 但是市场上…

操作系统基础---多线程

文章目录操作系统基础---多线程1.为何引入线程程序并发的时空开销线程的设计思路线程的状态和线程控制块TCB2.线程与进程的比较3.线程的实现⭐1.内核支持线程KST2.用户级线程3.组合方式操作系统基础—多线程 1.为何引入线程 利用传统的进程概念和设计方法已经难以设计出适合于…

方向导数与梯度下降

文章目录方向角与方向余弦方向角方向余弦方向导数定义性质梯度下降梯度下降法&#xff08;Gradient descent&#xff09;是一个一阶最优化算法&#xff0c;通常也称为最速下降法。 要使用梯度下降法找到一个函数的局部极小值&#xff0c;必须向函数上当前点对应梯度&#xff08…

学习数据库第一天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、数据库的开启和关闭二、数据库的登录 添加数据库以及表1.登录数据库2.查看数据库三 数据表前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#x…

spring cloud脚手架快速开发 微服务架构 JAVA敏捷开发框架源码

核心技术采用Spring Cloud Alibaba、SpringBoot、Mybatis、Seata、Sentinel、RabbitMQ、FastDFS/MinIO、SkyWalking等主要框架和中间件。 私信了解更多&#xff01; 功能模块&#xff1a; 1、租户管理&#xff1a;运营人员管理所有的租户创建 2、工作台&#xff1a;普通用户…

支持分布式部署的主流方式 - Session 持久化到 Redis

1.为什么要将 Session 存储在 Redis 中如果我们不将 Session 存储在 MySQL 或者 Redis 中, 那么做出来的项目就只能支持单机部署, 不支持分布式部署. 因为之前我们只是将 Session 存储在当前电脑的内存里面. 当张三去登录的时候, 将 Session 信息存储在 A 服务器, 这个时候负载…