【算法】最短路径——迪杰斯特拉 (Dijkstra) 算法

目录

  • 1.概述
  • 2.代码实现
    • 2.1.节点类
    • 2.2.邻接矩阵存储图
    • 2.3.邻接表存储图
    • 2.4.测试
  • 3.扩展
    • 3.1.只计算一对顶点之间的最短路径
    • 3.2.获取起点到其它节点具体经过的节点
  • 4.应用

本文参考:
LABULADONG 的算法网站

1.概述

(1)在图论中,最短路径是指在加权图中两个顶点之间长度最短的路径,这个路径的长度是每条边的权重之和。在现实生活中,可以将图中的顶点表示为地点,将边表示为这些地点之间的道路或交通线路,把每条边的权重定义为行程时间、行驶距离、经济成本、能源消耗等相应的度量单位。在这种情况下,最短路径问题就是为了找到从一个地点到另一个地点的最快、最短、最便宜、最节能的路径。最短路径问题在计算机科学和运筹学方面非常重要,它可以解决很多现实问题,如网页排名算法、路由算法、航班调度、电信网络建设等。Dijkstra 算法是解决最短路径问题的经典算法之一。

(2)迪杰斯特拉算法 (Dijkstra) 是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

(3)实现 Dijkstra 算法的一种基本思路如下:

  • 维护一个待确定最短路径的节点的集合,初始时只有起点。之后,每次从这个集合中取出一个节点,更新它所有邻居的距离,将它们加入这个集合中。具体实现中,使用一个优先队列来存储待访问的节点,并按照最短距离从小到大的顺序进行访问。
  • 在代码中,使用一个数组 dist 来记录起点到每个节点的最短距离,同时使用一个自定义的 Node 类来表示所有待访问的节点,并存储其与起点的距离。算法主体部分由一个 while 循环实现。每次取出队列中距离最小的节点,并遍历其所有邻居,更新起点到每个邻居的距离,然后将未确定最短路径的点加入队列中。

常数较小的情况下,Dijkstra 算法的时间复杂度为 O(ElogV),其中 E 为边数,V 为顶点数。

2.代码实现

2.1.节点类

class Node {//图中当前节点的 idint id;//从 start 节点到当前节点的距离int distFromStart;public Node(int id, int distFromStart) {this.id = id;this.distFromStart = distFromStart;}
}

2.2.邻接矩阵存储图

class Solution {/*start: 起点graph: 用于表示图的邻接矩阵返回值: 起点到图中每一个点的最短距离*/public int[] dijkstra(int start, int[][] graph) {// dist[i] 表示起点 start 到节点 i 的最短路径长度int[] dist = new int[graph.length];// dist[i] = Integer.MAX_VALUE 表示起点到节点 i 之间不可达Arrays.fill(dist, Integer.MAX_VALUE);//起点与自己之间的最短路径长度为 0dist[start] = 0;//自定义优先级队列规则,distFromStart 值较小的节点排在队首Queue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(a -> a.distFromStart));queue.offer(new Node(start, 0));while (!queue.isEmpty()) {//取出队首元素Node node = queue.poll();int id = node.id;int curDistFromStart = node.distFromStart;if (curDistFromStart > dist[id]) {continue;}//将与当前节点相邻的所有节点存入队列for (int i = 0; i < graph[id].length; i++) {if (graph[id][i] != Integer.MAX_VALUE) {int distToNextNode = dist[id] + graph[id][i];// 更新 distif (dist[i] > distToNextNode) {dist[i] = distToNextNode;queue.offer(new Node(i, distToNextNode));}}}}return dist;}
}

2.3.邻接表存储图

class Solution {/*start: 起点graph: 用于表示图的邻接表返回值: 起点到图中每一个点的最短距离*/public int[] dijkstra(int start, List<int[]>[] graph) {// dist[i] 表示起点 start 到节点 i 的最短路径长度int[] dist = new int[graph.length];// dist[i] = Integer.MAX_VALUE 表示起点到节点 i 之间不可达Arrays.fill(dist, Integer.MAX_VALUE);//起点与自己之间的最短路径长度为 0dist[start] = 0;//自定义优先级队列规则,distFromStart 值较小的节点排在队首Queue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(a -> a.distFromStart));queue.offer(new Node(start, 0));while (!queue.isEmpty()) {//取出队首元素Node node = queue.poll();int id = node.id;int curDistFromStart = node.distFromStart;if (curDistFromStart > dist[id]) {continue;}//将与当前节点相邻的所有节点存入队列for (int[] neighbor : graph[id]) {int nextNodeID = neighbor[0];int distToNextNode = dist[id] + neighbor[1];//更新 distif (dist[nextNodeID] > distToNextNode) {dist[nextNodeID] = distToNextNode;queue.offer(new Node(nextNodeID, distToNextNode));}}}return dist;}
}

2.4.测试

(1)本测试中的加权无向图如下所示,并且设置起点为 0。

在这里插入图片描述

(2)邻接矩阵的测试代码如下:

class Test {public static void main(String[] args) {//图的顶点数int n = 7;int[][] graph = new int[n][n];//初始化邻接矩阵,初始化为 Integer.MAX_VALUE 表示不可达for (int i = 0; i < n; i++) {Arrays.fill(graph[i], Integer.MAX_VALUE);}//添加图的边graph[0][1] = 9;graph[0][5] = 1;graph[1][0] = 9;graph[1][2] = 4;graph[1][6] = 3;graph[2][1] = 4;graph[2][3] = 2;graph[3][2] = 2;graph[3][4] = 6;graph[3][6] = 5;graph[4][3] = 6;graph[4][5] = 8;graph[4][6] = 7;graph[5][0] = 1;graph[5][4] = 8;graph[6][1] = 3;graph[6][3] = 5;graph[6][4] = 7;Solution solution = new Solution();int start = 0;int[] distances = solution.dijkstra(start, graph);System.out.println(Arrays.toString(distances));}
}

输出结果如下:

[0, 9, 13, 15, 9, 1, 12]

(3)邻接表的测试代码如下:

class Test {public static void main(String[] args) {//图的顶点数int n = 7; List<int[]>[] graph = new ArrayList[n];//初始化邻接表for (int i = 0; i < n; i++) {graph[i] = new ArrayList<>();}//添加图的边graph[0].add(new int[]{1, 9});graph[0].add(new int[]{5, 1});graph[1].add(new int[]{0, 9});graph[1].add(new int[]{2, 4});graph[1].add(new int[]{6, 3});graph[2].add(new int[]{1, 4});graph[2].add(new int[]{3, 2});graph[3].add(new int[]{2, 2});graph[3].add(new int[]{4, 6});graph[3].add(new int[]{6, 5});graph[4].add(new int[]{3, 6});graph[4].add(new int[]{5, 8});graph[4].add(new int[]{6, 7});graph[5].add(new int[]{0, 1});graph[5].add(new int[]{4, 8});graph[6].add(new int[]{1, 3});graph[6].add(new int[]{3, 5});graph[6].add(new int[]{4, 7});Solution solution = new Solution();int start = 0;int[] distances = solution.dijkstra(start, graph);System.out.println(Arrays.toString(distances));}
}

输出结果如下:

[0, 9, 13, 15, 9, 1, 12]

3.扩展

3.1.只计算一对顶点之间的最短路径

如果现在只需计算起点 start 到终点 end 的最短路径,那么只需要简单修改上述代码即可,以用邻接表存储图的代码为例:

class Solution {/*start: 起点graph: 用于表示图的邻接矩阵返回值: 起点 start 到终点 end 的最短路径*/public int dijkstra(int start, int end, int[][] graph) {//...while (!queue.isEmpty()) {//取出队首元素Node node = queue.poll();int id = node.id;int curDistFromStart = node.distFromStart;//添加如下代码:如果遍历到 end,直接返回 curDistFromStart 即可if (id == end) {return curDistFromStart;}if (curDistFromStart > dist[id]) {continue;}//... }//如果运行到这里,说明 start 到 end 之间不可达return Integer.MAX_VALUE;}
}

3.2.获取起点到其它节点具体经过的节点

(1)如果需要找到起点到其余节点的最短路径中依次经过的节点,可以在 Dijkstra 算法中添加一个 prev 数组或 map,记录节点i的前一个访问过的节点 j。在更新 dist[i] 的同时,同时更新 prev[i] = j。最后,通过回溯 prev 数组,可以从目标节点往回遍历,找到最短路径上的所有节点。具体来说,可以按以下步骤实现:

  • 初始化 prev 数组,将所有节点的前继节点都设置为起点。
  • 在更新 dist[i] 的同时,同时更新 prev[i] = j。
  • 当所有节点都处理完毕后,就可以从目标节点往回遍历 prev 数组,找到最短路径上的所有节点。

(2)以用邻接表存储图的代码为例,具体代码如下所示:

class Solution {/*start: 起点graph: 用于表示图的邻接表返回值: 起点到图中每一个点的最短距离依次所经过的节点*/public List<List<Integer>> findShortestPaths(int start, List<int[]>[] graph) {int n = graph.length;int[] dist = new int[n];Arrays.fill(dist, Integer.MAX_VALUE);dist[start] = 0;int[] prev = new int[n];Arrays.fill(prev, start);Queue<Node> queue = new PriorityQueue<>(Comparator.comparingInt(a -> a.distFromStart));queue.offer(new Node(start, 0));while (!queue.isEmpty()) {Node node = queue.poll();int id = node.id;int curDistFromStart = node.distFromStart;if (curDistFromStart > dist[id]) {continue;}for (int[] neighbor : graph[id]) {int nextNodeID = neighbor[0];int distToNextNode = dist[id] + neighbor[1];if (dist[nextNodeID] > distToNextNode) {//在更新 dist[nextNodeID] 时,同时更新 prev[nextNodeID]dist[nextNodeID] = distToNextNode;prev[nextNodeID] = id;queue.offer(new Node(nextNodeID, distToNextNode));}}}//通过 prev 数组回溯路径List<List<Integer>> paths = new ArrayList<>();for (int i = 0; i < n; i++) {List<Integer> path = new ArrayList<>();int curNode = i;while (curNode != start) {path.add(curNode);curNode = prev[curNode];}path.add(start);Collections.reverse(path);paths.add(path);}return paths;}
}

(3)测试代码如下:

class Solution {public static void main(String[] args) {//图的顶点数int n = 7;List<int[]>[] graph = new ArrayList[n];//初始化邻接表for (int i = 0; i < n; i++) {graph[i] = new ArrayList<>();}//添加图的边graph[0].add(new int[]{1, 9});graph[0].add(new int[]{5, 1});graph[1].add(new int[]{0, 9});graph[1].add(new int[]{2, 4});graph[1].add(new int[]{6, 3});graph[2].add(new int[]{1, 4});graph[2].add(new int[]{3, 2});graph[3].add(new int[]{2, 2});graph[3].add(new int[]{4, 6});graph[3].add(new int[]{6, 5});graph[4].add(new int[]{3, 6});graph[4].add(new int[]{5, 8});graph[4].add(new int[]{6, 7});graph[5].add(new int[]{0, 1});graph[5].add(new int[]{4, 8});graph[6].add(new int[]{1, 3});graph[6].add(new int[]{3, 5});graph[6].add(new int[]{4, 7});Solution solution = new Solution();int start = 4;List<List<Integer>> paths = solution.findShortestPaths(start, graph);for (int i = 0; i < n; i++) {System.out.println("从节点 " + start + " 到节点 " + i +" 的最短距离经过的节点依次为: " + paths.get(i));}}
}

输出结果如下:

从节点 4 到节点 0 的最短距离经过的节点依次为: [4, 5, 0]
从节点 4 到节点 1 的最短距离经过的节点依次为: [4, 6, 1]
从节点 4 到节点 2 的最短距离经过的节点依次为: [4, 3, 2]
从节点 4 到节点 3 的最短距离经过的节点依次为: [4, 3]
从节点 4 到节点 4 的最短距离经过的节点依次为: [4]
从节点 4 到节点 5 的最短距离经过的节点依次为: [4, 5]
从节点 4 到节点 6 的最短距离经过的节点依次为: [4, 6]

4.应用

(1)Dijkstra算法是一种用于解决单源最短路径问题的算法。它可以帮助找到从一个源节点到图中所有其他节点的最短路径。这个算法广泛应用于许多领域,包括以下几个方面:

  • 网络路由:Dijkstra 算法在网络路由中被广泛使用,用于计算最短路径来传输数据包。
  • 交通规划:Dijkstra 算法可以用于交通网络中的最短路径规划,例如在城市道路网络中找到最短驾驶路线。
  • 电信网络:Dijkstra 算法可以用于计算通信网络中的最短路径,例如电话网络或互联网中的数据包传输。
  • 地理信息系统 (GIS):Dijkstra 算法可以用于计算地理信息系统中的最短路径,例如导航系统中找到最佳行驶路径。
  • 运输和物流:Dijkstra 算法可以用于解决运输和物流问题,例如货物配送中最优路径的规划。

(2)大家可以去 LeetCode 上找相关的 Dijkstra 算法的题目来练习,或者也可以直接查看 LeetCode算法刷题目录 (Java) 这篇文章中的最短路径章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

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

相关文章

iOS OpenGL ES3.0入门实践

一、效果图 入门实践&#xff0c;做的东西比较简单&#xff0c;效果如下&#xff1a; 二、关于顶点坐标和纹理坐标 绘制图片需要设置顶点坐标和纹理坐标并加载像素数据&#xff0c;之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系&#xff0c;就是告诉OpenGL&#xf…

Spring整合redis的key时出现\xac\xed\x00\x05t\前缀问题

AutowiredRedisTemplate redisTemplate;User usernew User(5,"tomhs","tttt");ValueOperations opsForValue redisTemplate.opsForValue();//存放key,opsForValue.set("user"user.getId(),user);//读取数据;System.out.println(opsForValue.get…

NLP领域的突破催生大模型范式的形成与发展

当前的大模型领域的发展&#xff0c;只是范式转变的开始&#xff0c;基础大模型才刚刚开始改变人工智能系统在世界上的构建和部署方式。 1、大模型范式 1.1 传统思路&#xff08;2019年以前&#xff09; NLP领域历来专注于为具有挑战性的语言任务定义和设计系统&#xff0c…

【广州华锐互动】VR居家防火逃生模拟演练增强训练的真实性

VR软件开发公司广州华锐互动在消防培训领域已开发了多款VR产品&#xff0c;今天为大家介绍VR居家防火逃生模拟演练系统&#xff0c;这是一种基于虚拟现实技术的消防教育训练设备&#xff0c;通过模拟真实的火灾场景&#xff0c;让使用者身临其境地体验火灾逃生过程&#xff0c;…

qemu 之 uboot、linux 启动

目录 编译uboot、kernel 编译启动从 uboot 中引导启动 linux注参考 本文主要说明 arm64 在 qemu 上的相关启动。 编译 使用的是 qemu-8.1.1 版本&#xff0c;编译命令如下: ../configure --cc/usr/local/bin/gcc --prefix/home/XXX/qemu_out --enable-virtfs --enable-slir…

基于安卓android微信小程序的食谱大全系统

项目介绍 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个开发过程首先对食谱大全进行需求分析&#xff0c;得出食谱大全主要功能。接着对食谱大全进行总体设计和详细设计。总体设…

多种格式图片可用的二维码生成技巧,快来学习一下

将图片存入二维码是现在很常见的一种图片展现方式&#xff0c;有效的节省了图片占用内容空间以及获取图片内容的速度&#xff0c;所以现在会有很多人将不同的图片、照片生成二维码展示。如何使用图片二维码生成器来快速生成二维码呢&#xff1f;下面就让小编来给大家分享一下图…

大数据分析师职业技能提升好考吗?含金量高不高

随着大数据时代的到来&#xff0c;大数据分析技能需求已经成为很多企业和机构的必备要求。大数据分析师证书成为当下的热门之一&#xff0c;那么大数据分析师证书需要具备哪些条件呢&#xff1f; 首先&#xff0c;报考大数据分析师证书需要具备以下方面的条件&#xff1a; …

(C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。

要求&#xff1a;原始数组的数据从键盘随机输入&#xff0c;新数组以4行4列的方式输出。 #include<stdio.h> int main() {int matrix[4][4],matrix2[4][4];int count;for(int i 0;i < 4;i )for(int j 0;j < 4;j )scanf("%d",&matrix[i][j]);for(i…

读书充电,温暖你的冬日,本期为大家送出几本架构师成长和软件架构技术相关的好书,助你度过这个不太景气的寒冬!

目图书录 ⭐️《高并发架构实战&#xff1a;从需求分析到系统设计》⭐️《架构师的自我修炼&#xff1a;技术、架构和未来》⭐️《中台架构与实现&#xff1a;基于DDD和微服务》⭐️《分布式系统架构&#xff1a;架构策略与难题求解》⭐️《流程自动化实战&#xff1a;系统架构…

Java学习笔记(七)——面向对象编程(中级)

一、IDEA &#xff08;一&#xff09;常用的快捷键 &#xff08;二&#xff09;模版/自定义模版 二、包 &#xff08;一&#xff09;包的命名 &#xff08;二&#xff09;常用的包 &#xff08;三&#xff09;如何引入&#xff08;导入&#xff09;包 &#xff08;四&am…

Linux系统编程,Linux中的文件读写文件描述符

文章目录 Linux系统编程&#xff0c;Linux中的文件读写操作1.open函数&#xff0c;打开文件 Linux系统编程&#xff0c;Linux中的文件读写操作 1.open函数&#xff0c;打开文件 我们来看下常用的open函数 这个函数最终返回一个文件描述符struct file 我们查看一下它的Ubuntu…

.NET快速对接极光消息推送

什么是消息推送&#xff1f; 很多手机APP会不定时的给用户推送消息&#xff0c;例如一些新闻APP会给用户推送用户可能感兴趣的新闻&#xff0c;或者APP有更新了&#xff0c;会给用户推送是否选择更新的消息等等&#xff0c;这就是所谓的“消息推送”。 常见的一些APP消息推送…

【Vue】【uni-app】工单管理页面实现

用的是uni-app的uni-ui拓展组件实现的 功能是对工单进行一个展示&#xff0c;并对工单根据一些筛选条件进行搜索 目前是实现了除了日期之外的搜索功能&#xff0c;测试数据是下面这个tableData.js&#xff0c;都是我自己手写的&#xff0c;后端请求也稍微写了一些&#xff0c;…

vue3 el-menu初始化时选中没有高亮的问题(default-active和index的问题)

首先看官方文档的示例&#xff1a; 需要注意的是&#xff1a; 1、default-active的值是字符串&#xff0c;那么index绑定的值也要是字符串&#xff0c;且数字对应。不能default-avtive绑定的是1&#xff0c;而menu-item的index绑定的是45 2、default-active的值是当前选中me…

C++面向对象编程(4)——浅谈C++内存模型

目录 一. 说明 二. GDB实验 2.1 实验1&#xff1a;栈 2.2 实验2&#xff1a;堆 一. 说明 不同的操作系统对程序内存的管理和划分会有所不同。如上图所示的C内存区域划分主要是针对一般的情况&#xff0c;说明如下&#xff1a; 1. Stack&#xff1a;栈。由编译器管理分配和回…

远程登录Linux方法(Linux平台相互远程;Windows远程登录Linux、远程编码、文件传输;无法远程登录的问题解决;c程序的编译)

在实际使用Linux系统过程中我们不可避免的需要远程登录Linux&#xff0c;这是因为未来大家使用Linux服务器的时候你所对应的那台Linux服务器不一定提供界面(服务器可能在外地)。本篇将会介绍远程登录Linux的方法。 文章目录 1. SSH介绍2. Linux平台相互远程及文件传输2.1 Linux…

MySQL MHA高可用切换

MySQL MHA 1&#xff0e;什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在…

【PG】PostgreSQL高可用方案repmgr部署(非常详细)

目录 简介 1 概述 1.1 术语 1.2 组件 1.2.1 repmgr 1.2.2 repmgrd 1.3 Repmgr用户与元数据 2 安装部署 2.0 部署环境 2.1 安装要求 2.1.1 操作系统 2.1.2 PostgreSQL 版本 2.1.3 操作系统用户 2.1.4 安装位置 2.1.5 版本要求 2.2 安装 2.2.1 软件包安装 2.2…

【C++】日期类实现,与日期计算相关OJ题

文章目录 日期类的设计日期计算相关OJ题HJ73 计算日期到天数转换KY111 日期差值KY222 打印日期KY258 日期累加 在软件开发中&#xff0c;处理日期是一项常见的任务。为了方便地操作日期&#xff0c;我们可以使用C编程语言来创建一个简单的日期类。在本文中&#xff0c;我们将介…