Dijkstra算法——单源最短路径(指定一个节点(源点)到其余各个顶点的最短路径)

2023-10-13 01:20

本文主要是介绍Dijkstra算法——单源最短路径(指定一个节点(源点)到其余各个顶点的最短路径),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Dijkstra算法——单源最短路径

  • 1.预设场景
  • 2.数据结构描述
  • 3.算法基本思想
  • 具体过程详解
  • 4.代码实现
  • 5.总结
  • 6.END!

1.预设场景

国庆期间,小明打算从1号城市出发,在五天假期中分别去往不同的城市(2,3,4,5,6)旅游,为减轻负担,他想要知道1号城市到各个城市之间的最短距离。
在这里插入图片描述
现在需要设计一种算法求得源点到任意一个城市之间的最短路径。该问题的求解也被称为“单源最短路径”。

2.数据结构描述

在所有的数据结构中,0号下标(0行0列)均不存储元素
同样,这里使用二维数组e来存储顶点之间边的关系。初始值如下:
在这里插入图片描述
用一个一维数组dis存储源点(1号顶点)到其余各个顶点的初始距离。其初始值如下:
在这里插入图片描述

3.算法基本思想

算法的基本思想是:每次找到距离源点最近的一个节点,然后以该节点为中心进行扩展,最终得到源点到其余所有点的最短路径。具体步骤如下:

  1. 将所有的顶点分为两部分:已知最短路径的顶点集合P和未知最短路径的顶点集合Q。开始,已知最短路径的顶点集合P中只有源点一个节点,知最短路径的顶点集合Q中包含了除了源点之外的所有节点。为减少空间复杂度(P和Q不重新开辟空间),用一个数组book记录那个节点在P中,那个节点在Q中。例如对于节点i来说,book[i] = 1表示节点i在集合p中,book[i] = 0表示节点i在集合Q中。
  2. 设置源点i到自己的最短路径为0,即dis[i] = 0。若存在有源点能直接到达的顶点j,则把dis[j] = e[i][j],同时把其他源点不能直接到达顶点的最短路径设为∞。
  3. 在集合Q的所有顶点中选择一个离源点i最近的顶点u(dis[u]最小)加入到集合P。然后考察所有以u为起点的边,对每一条边进行松弛操作。(何为松弛操作:例如存在一条从u到j的边,那么就可以以u为中间节点到达j,这条路径的长度是e[u][j] + dis[u],如果这个值比目前的dis[j]小,可以就找到了一个i到j的最短路径,用新值代替当前dis[j]中的值。)
  4. 重复步骤3,直至集合Q为空。最终数组dis中的值就是源点到所有顶点的最短路径。

具体过程详解

还是以下面的图为例:
在这里插入图片描述
,其初始化二维数组e和数组dis皆如上所示,没有任何变化。
在这里插入图片描述
在这里插入图片描述

具体步骤如下:

  1. 初始化e[vertice + 1][vertice + 1],book[vertice + 1](集合P和集合Q),和dis[vertice + 1](如上所示)。

  2. 观察比较数组dis,可以得到dis[2]为数组dis中的最小值,而且book[2]==0。所以选择2号节点。对儿2号节点来说,有e[2][3] = 9和e[2][4] = 3。即从2号节点出发可以到达3,4号两个节点。
    ①dis[3]==12 >(dis[2] + e[2][3] == 10),所以可以判断1号顶点->3号顶点(dis[3])的距离 大于 1号顶点->2号顶点->3号顶点,于是更新dis[3]的值。
    ②dis[4]==∞ >(dis[2] + e[2][4] == 4),所以可以判断1号顶点->4号顶点(dis[3])的距离 大于 1号顶点->2号顶点->4号顶点,于是更新dis[3]的值。
    至此,以2号节点的边来“松弛”过程结束,数据结构变化如下:
    在这里插入图片描述

  3. 观察步骤2后的结果,可以发现dis[4]为数组dis中的最小值,而且book[4]==0。所以选择4号节点。对儿4号节点来说,有e[4][3] = 4、e[4][5] =13和e[4][6] = 15。即从4号节点出发可以到达3,5,6号三个节点。
    ①dis[3]==10 >(dis[4] + e[4][3] == 8),所以可以判断1号顶点->2号顶点->3号顶点(dis[3])的距离 大于 1号顶点->2号顶点->4号顶点->3号顶点,于是更新dis[3]的值。(2号顶点为上一次的更新过程)
    ②dis[5]==∞ >(dis[4] + e[4][5] == 17),所以可以判断1号顶点->5号顶点(dis[5])的距离 大于 1号顶点->4号顶点->5号顶点,于是更新dis[5]的值。
    ③dis[6]==∞ >(dis[4] + e[4][6] == 19),所以可以判断1号顶点->6号顶点(dis[6])的距离 大于 1号顶点->4号顶点->6号顶点,于是更新dis[6]的值。
    至此,以4号节点的边来“松弛”过程结束,数据结构变化如下:
    在这里插入图片描述
    4.观察步骤3后的结果,可以发现dis[3]为数组dis中的最小值,而且book[3]==0。所以选择3号节点。对儿3号节点来说,有e[3][5] = 5。即从3号节点出发可以到达5号节点。
    ①dis[5]==17 >(dis[3] + e[3][5] == 13),所以可以判断1号顶点->2号顶点->4号顶点->5号顶点(dis[5])的距离 大于 1号顶点->3号顶点->5号顶点,于是更新dis[3]的值。(2,4号顶点为上一次的更新过程)
    至此,以3号节点的边来“松弛”过程结束,数据结构变化如下:
    在这里插入图片描述
    5.观察步骤4后的结果,可以发现dis[5]为数组dis中的最小值,而且book[5]==0。所以选择5号节点为中间节点。对儿4号节点来说,有e[5][6] = 4。即从5号节点出发可以到达6号节点。
    ①dis[6]==19 >(dis[5] + e[5][6] == 17),所以可以判断1号顶点->2号顶点->4号顶点->6号顶点(dis[6])的距离 大于 1号顶点->2号顶点->4号顶点->3号顶点->5号顶点->6号顶点,于是更新dis[6]的值。(2,4号顶点为上一次的更新过程)
    至此,以5号节点的边来“松弛”过程结束,数据结构变化如下:
    在这里插入图片描述
    6.观察步骤5后的结果,可以发现dis[6]为数组dis中的最小值,而且book[6]==0。所以选择6号节点为中间节点。但是对于6号节点来说,没有可以扩展的节点。
    至此,以6号节点的边来“松弛”过程结束,数据结构变化如下:
    在这里插入图片描述
    最后,数组dis如下,这便是1号顶点到其余顶点之间的最短距离:在这里插入图片描述

4.代码实现

#include <vector>
#include <iostream>using namespace std;class Digkstra
{
private:int vertice = 0;//顶点数int edge = 0;//边数vector<vector<int>> e;vector<bool> book;//判断顶点j是否扩展过vector<int> dis;//源点到各个顶点之间的最短距离public:Digkstra(int x, int y) :vertice(x), edge(y){//图的初始化从下标1开始e.resize(vertice + 1);//初始化二维数组的行for (int i = 0; i <= vertice; i++){e[i].resize(vertice + 1);//初始化二维数组的列}dis.resize(vertice + 1);book.resize(vertice + 1);}//图的初始化void Init_Digkstra(){for (int i = 0;i <= vertice; i++){for (int j = 0; j <= vertice; j++){if (i == 0 || j == 0){e[i][j] = 0;}if (i == j){e[i][j] = 0;}else{e[i][j] = INT_MAX;}}}}//读入图的边,并且根据边的信息初始化数组dis,数组bookvoid GetEdgeInfo(){cout << "输入边的信息(节点1,节点2,权重):" << endl;int e1 = 0, e2 = 0, weigth = 0;for (int i = 1; i <= edge; i++){cin >> e1 >> e2 >> weigth;e[e1][e2] = weigth;}for (int i = 1; i <= vertice; i++){dis[i] = e[1][i];}book[1] = true;}//打印void Print(){for (int i = 1; i <= vertice; i++){cout << dis[i] << "    "; }         cout << endl;}//Digkstra核心思想void Digkstra_Alg(){int u = 0;//离1号顶点最近顶点的下标for (int k = 1; k <= vertice; k++){int min = INT_MAX;//找离1号节点最近的节点(找数组dis中的最小值)           for (int j = 1; j <= vertice; j++){if (book[j] == false && dis[j] < min){min = dis[j];u = j;}}book[u] = true;for (int i = 1; i <= vertice; i++){if (e[u][i] < INT_MAX){if (dis[i] > dis[u] + e[u][i]){dis[i] = dis[u] + e[u][i];}}}}}};int main()
{Digkstra Digkstra(6, 9);Digkstra.Init_Digkstra();Digkstra.GetEdgeInfo();cout << "初始信息:" << endl;Digkstra.Print();Digkstra.Digkstra_Alg();cout << "单源最短路径(顶点1到其余各顶点):" << endl;Digkstra.Print();return 0;
}

在这里插入图片描述

5.总结

通过代码,可以得到该算法的时间复杂度是O(N^2)。而且这是一种基于贪心策略的算法。每次扩展一个新的最短距离的节点,就要更新与其相邻的点的距离。当所有边权重为正时,由于不存在一个路程更短的没有被扩展的点,所以这个点的距离不会别再次改变,从而保证了算法的正确性。
根据这一特点,用此算法求最短路径的图是不能有负权重的,因为扩展到负权重边的时候会产生更短的距离,有可能破坏已经更新的点距离不会改变的性质。

6.END!

这篇关于Dijkstra算法——单源最短路径(指定一个节点(源点)到其余各个顶点的最短路径)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/199763

相关文章

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

通过C#获取PDF中指定文本或所有文本的字体信息

《通过C#获取PDF中指定文本或所有文本的字体信息》在设计和出版行业中,字体的选择和使用对最终作品的质量有着重要影响,然而,有时我们可能会遇到包含未知字体的PDF文件,这使得我们无法准确地复制或修改文... 目录引言C# 获取PDF中指定文本的字体信息C# 获取PDF文档中用到的所有字体信息引言在设计和出

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1

多模块的springboot项目发布指定模块的脚本方式

《多模块的springboot项目发布指定模块的脚本方式》该文章主要介绍了如何在多模块的SpringBoot项目中发布指定模块的脚本,作者原先的脚本会清理并编译所有模块,导致发布时间过长,通过简化脚本... 目录多模块的springboot项目发布指定模块的脚本1、不计成本地全部发布2、指定模块发布总结多模

VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virtual disk”问题

《VMWare报错“指定的文件不是虚拟磁盘“或“Thefilespecifiedisnotavirtualdisk”问题》文章描述了如何修复VMware虚拟机中出现的“指定的文件不是虚拟... 目录VMWare报错“指定的文件不是虚拟磁盘“或“The file specified is not a virt

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

python获取当前文件和目录路径的方法详解

《python获取当前文件和目录路径的方法详解》:本文主要介绍Python中获取当前文件路径和目录的方法,包括使用__file__关键字、os.path.abspath、os.path.realp... 目录1、获取当前文件路径2、获取当前文件所在目录3、os.path.abspath和os.path.re

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06