【算法基础实验】图论-Dijkstra最短路径

news/2024/9/20 7:20:12/ 标签: 算法, 图论, java, 最短路径, Dijkstra

理论知识

边的放松

在这里插入图片描述

边的放松(Edge Relaxation)是图算法中的一个关键操作,主要用于解决最短路径问题。它的核心思想是在遍历图的过程中,通过比较和更新路径的长度,逐步找到从起点到每个顶点的最短路径

边的放松过程

假设我们有一个从顶点 v 到顶点 w 的边 e,其权重为 weight(v, w)。边的放松操作如下:

  1. 检查当前路径:检查通过 v 到达 w 的路径是否比已经记录的从起点到 w 的路径更短。
  2. 更新路径:如果通过 v 到达 w 的路径更短,则更新 w 的最短路径长度,并将 w 的前驱顶点设置为 v。

数学表达式为:

IF

d i s t T o [ w ] > d i s t T o [ v ] + w e i g h t ( v , w ) distTo[w]>distTo[v]+weight(v,w) distTo[w]>distTo[v]+weight(v,w)

THEN

d i s t T o [ w ] = d i s t T o [ v ] + w e i g h t ( v , w ) distTo[w]=distTo[v]+weight(v,w) distTo[w]=distTo[v]+weight(v,w)

e d g e T o [ w ] = e edgeTo[w]=e edgeTo[w]=e

其中:

  • distTo[w] 表示从起点到 w 的当前已知最短路径长度。
  • distTo[v] + weight(v, w) 表示通过 v 到达 w 的路径长度。
  • edgeTo[w] 表示当前最短路径中 w 的前驱节点。

已知树结点所对应的 distTo[] 值均为最短路径的长度。对于优先队列中的任意顶点 w,distTo[w]是从 s 到 w 的最短路径的长度,该路径上的中间顶点在树中且路径结束于横切边edgeTo[w]。优先级最小的顶点的 distTo[] 值就是最短路径的权重,它不会小于已经被放松过的任意顶点的最短路径的权重,也不会大于还未被放松过的任意顶点的最短路径的权重。这个顶点就是下一个要被放松的顶点。所有从 s 可达的顶点都会按照最短路径的权重顺序被放松。
在这里插入图片描述

实验数据

java">8
15
4 5 0.35
5 4 0.35
4 7 0.37
5 7 0.28
7 5 0.28
5 1 0.32
0 4 0.38
0 2 0.26
7 3 0.39
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93

算法轨迹

算法处理样图 tinyEWD.txt 时的轨迹。在这个例子中,算法构造最短路径
树的过程如下所述。
将顶点 0 添加到树中,将顶点 2 和 4 加入优先队列。
从优先队列中删除顶点 2,将 0 → 2 添加到树中,将顶点 7 加入优先队列。
从优先队列中删除顶点 4,将 0 → 4 添加到树中,将顶点 5 加入优先队列,边
4 → 7 失效。
从优先队列中删除顶点 7,将 2 → 7 添加到树中,将顶点 3 加入优先队列,边
7 → 5 失效。
从优先队列中删除顶点 5,将 4 → 5 添加到树中,将顶点 1 加入优先队列,边
5 → 7 失效。
从优先队列中删除顶点 3,将 7 → 3 添加到树中,将顶点 6 加入优先队列。
从优先队列中删除顶点 1,将 5 → 1 添加到树中,边 1 → 3 失效。
从优先队列中删除顶点 6,将 3 → 6 添加到树中。
在这里插入图片描述

代码实现

java">import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;public class myDijkstraSP {private myDirectedEdge[] edgeTo;private double[] distTo;private myIndexMinPQ<Double> pq;public myDijkstraSP(myEdgeWeightedDigraph G, int s){edgeTo = new myDirectedEdge[G.V()];distTo = new double[G.V()];pq = new myIndexMinPQ<Double>(G.V());for(int v = 0; v<G.V(); v++)distTo[v] = Double.POSITIVE_INFINITY;distTo[s] = 0.0;pq.insert(s,0.0);while(!pq.isEmpty())relax(G,pq.delMin());}private void relax(myEdgeWeightedDigraph G, int v){for(myDirectedEdge e:G.adj(v)){int w = e.to();if(distTo[w] > distTo[v] + e.weight()){distTo[w] = distTo[v] + e.weight();edgeTo[w] = e;if(pq.contains(w)) pq.change(w,distTo[w]);else pq.insert(w,distTo[w]);}}}public double distTo(int v) {return distTo[v];}public boolean hasPathTo(int v){ return distTo[v]<Double.POSITIVE_INFINITY;}public Iterable<myDirectedEdge> pathTo(int v){if(!hasPathTo(v)) { return null; }myLInkedStack<myDirectedEdge> path = new myLInkedStack<myDirectedEdge>();for(myDirectedEdge e = edgeTo[v];e!=null;e = edgeTo[e.from()])path.push(e);return path;}public static void main(String[] args){myEdgeWeightedDigraph G = new myEdgeWeightedDigraph(new In(args[0]));int s = Integer.parseInt(args[1]);myDijkstraSP sp = new myDijkstraSP(G,s);for(int i = 0; i<G.V(); i++){StdOut.print(s + " to " + i);StdOut.printf(" (%.2f): ",sp.distTo(i));if(sp.hasPathTo(i))for(myDirectedEdge e:sp.pathTo(i))StdOut.print(e + "  ");StdOut.println();}}
}

代码详解

这段代码实现了 Dijkstra 最短路径算法,用于计算加权有向图中从给定起点到所有其他顶点的最短路径Dijkstra 算法是一种广泛应用的图算法,特别适用于边权重非负的图。下面我们将详细讲解代码的各个部分。

类变量和构造函数

java">private myDirectedEdge[] edgeTo;  // 存储从起点到每个顶点的最短路径上的最后一条边
private double[] distTo;          // 存储从起点到每个顶点的最短路径长度
private myIndexMinPQ<Double> pq;  // 索引优先队列,用于动态找到具有最小distTo值的顶点public myDijkstraSP(myEdgeWeightedDigraph G, int s) {edgeTo = new myDirectedEdge[G.V()];distTo = new double[G.V()];pq = new myIndexMinPQ<Double>(G.V());for (int v = 0; v < G.V(); v++)distTo[v] = Double.POSITIVE_INFINITY;distTo[s] = 0.0;pq.insert(s, 0.0);while (!pq.isEmpty())relax(G, pq.delMin());
}

edgeTo:存储到达每个顶点的最短路径的最后一条边,用于在构建路径时回溯。
distTo:存储从起点 s 到每个顶点的最短路径长度。初始化为正无穷大(表示未知路径),起点 s 的距离设置为 0.0。
pq:索引优先队列,用于在算法执行过程中动态获取当前距离最短的顶点。
构造函数:初始化 edgeTo 和 distTo 数组,设置起点 s 的初始距离,并将 s 插入优先队列,然后开始循环放松(relax)过程,逐步确定最短路径
relax 方法

java">private void relax(myEdgeWeightedDigraph G, int v) {for (myDirectedEdge e : G.adj(v)) {int w = e.to();if (distTo[w] > distTo[v] + e.weight()) {distTo[w] = distTo[v] + e.weight();edgeTo[w] = e;if (pq.contains(w)) pq.change(w, distTo[w]);else pq.insert(w, distTo[w]);}}
}

功能:relax 方法用于“放松”从顶点 v 出发的所有边。对于每条边 e(从 v 到 w),如果通过 v 到达 w 的路径比当前已知的最短路径更短,则更新 distTo[w] 和 edgeTo[w],并更新优先队列 pq。
放松操作:如果通过 v 到达 w 的路径更短(即 distTo[v] + e.weight() 小于 distTo[w]),就更新 distTo[w] 和 edgeTo[w],并调整优先队列中的位置。
其他方法

java">public double distTo(int v) { return distTo[v]; }public boolean hasPathTo(int v) { return distTo[v] < Double.POSITIVE_INFINITY; }public Iterable<myDirectedEdge> pathTo(int v) {if (!hasPathTo(v)) { return null; }myLInkedStack<myDirectedEdge> path = new myLInkedStack<myDirectedEdge>();for (myDirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()])path.push(e);return path;
}

distTo(int v):返回从起点 s 到顶点 v 的最短路径长度。
hasPathTo(int v):检查从起点 s 到顶点 v 是否存在路径(即 distTo[v] 是否为正无穷大)。
pathTo(int v):返回从起点 s 到顶点 v 的最短路径。通过回溯 edgeTo 数组构建路径。
main 方法

java">public static void main(String[] args) {myEdgeWeightedDigraph G = new myEdgeWeightedDigraph(new In(args[0]));int s = Integer.parseInt(args[1]);myDijkstraSP sp = new myDijkstraSP(G, s);for (int i = 0; i < G.V(); i++) {StdOut.print(s + " to " + i);StdOut.printf(" (%.2f): ", sp.distTo(i));if (sp.hasPathTo(i))for (myDirectedEdge e : sp.pathTo(i))StdOut.print(e + "  ");StdOut.println();}
}

main 方法:从文件读取有向加权图 G,然后调用 myDijkstraSP 类计算从给定起点 s 出发的最短路径
打印结果:对于每个顶点 i,打印从起点 s 到 i 的最短路径和路径长度。如果路径存在,则按顺序打印路径上的每条边。
总结
这段代码实现了 Dijkstra 最短路径算法。通过优先队列 myIndexMinPQ,代码能够高效地找到当前最短路径,并通过 relax 操作不断优化和更新路径信息。最终,算法能够求得从给定起点到图中所有其他顶点的最短路径,并通过 pathTo 方法返回最短路径的具体边。

实验步骤

实验步骤

java">
C:\Users\xyz\IdeaProjects\algrithoms\src>javac myDijkstraSP.java C:\Users\xyz\IdeaProjects\algrithoms\src>java myDijkstraSP data\tinyEWD.txt 0
0 to 0 (0.00): 
0 to 1 (1.05): 0 -> 4 0.38  4 -> 5 0.35  5 -> 1 0.32
0 to 2 (0.26): 0 -> 2 0.26
0 to 3 (0.99): 0 -> 2 0.26  2 -> 7 0.34  7 -> 3 0.39
0 to 4 (0.38): 0 -> 4 0.38
0 to 5 (0.73): 0 -> 4 0.38  4 -> 5 0.35
0 to 6 (1.51): 0 -> 2 0.26  2 -> 7 0.34  7 -> 3 0.39  3 -> 6 0.52
0 to 7 (0.60): 0 -> 2 0.26  2 -> 7 0.34

辅助方法

myDirectedEdge

java">public class myDirectedEdge {private int v;private int w;private final double weight;public myDirectedEdge(int v, int w, double weight){this.v = v;this.w = w;this.weight = weight;}public double weight(){ return weight; }public int from(){ return v; }public int to(){ return w; }public String toString(){ return String.format("%d -> %d %.2f",v,w,weight); }
}

myEdgeWeightedDigraph

java">import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;public class myEdgeWeightedDigraph {private myBag<myDirectedEdge>[] adj;private int V;private int E;private static final String NEWLINE = System.getProperty("line.separator");public myEdgeWeightedDigraph(int V){this.V = V;this.E = 0;adj = (myBag<myDirectedEdge>[]) new myBag[V];for(int v=0;v<V;v++)adj[v] = new myBag<myDirectedEdge>();}public myEdgeWeightedDigraph(In in){this(in.readInt());int E = in.readInt();for(int i = 0; i<E; i++){int v = in.readInt();int w = in.readInt();double weight = in.readDouble();myDirectedEdge e = new myDirectedEdge(v,w,weight);addEdge(e);}}public void addEdge(myDirectedEdge e){adj[e.from()].add(e);E++;}public int V() {return V;}public int E() {return E;}public Iterable<myDirectedEdge> adj(int v) { return adj[v]; }public Iterable<myDirectedEdge> edges(){myBag<myDirectedEdge> bag = new myBag<myDirectedEdge>();for(int v = 0;v<V;v++)for(myDirectedEdge e:adj[v])bag.add(e);return bag;}public String toString() {StringBuilder s = new StringBuilder();s.append(V + " vertexes " + E + " edges " + NEWLINE);for (int v = 0; v < V; v++) {s.append(v + ": ");for (myDirectedEdge e : adj[v]) {s.append(e + "  ");}s.append(NEWLINE);}return s.toString();}public static void main(String[] args){In in = new In(args[0]);myEdgeWeightedDigraph G = new myEdgeWeightedDigraph(in);StdOut.println(G);}
}

http://www.ppmy.cn/news/1519106.html

相关文章

使用 Pandas 进行数据可视化:全面指南(六)

在数据分析的过程中,数据的可视化是一个至关重要的环节。通过图形展示数据,不仅能够帮助我们直观地理解数据,还能够揭示数据背后的规律和趋势。Pandas 作为 Python 生态系统中强大的数据分析库,不仅提供了数据处理和分析的功能,还内置了方便易用的可视化方法。本文将详细介…

AD19基础应用技巧:捕捉对象功能的讲解鼠标”绿色十字”大光标、小光标切换

AD PCB 中心点捕捉功能&#xff1a; 线段、圆、边框中心点捕捉。 有时候不想要鼠标自动捕捉中心点怎么办&#xff1f; 关于Altium Designer 20 的捕抓功能的讲解&#xff08;https://blog.csdn.net/weixin_44599693/article/details/126177841&#xff09; ——- AD PCB画板…

服务器上部署Wordpress:Docker技术教程

今天在三丰云免费服务器上进行部署测试&#xff0c;这款不错的免费服务器配置为1核CPU、1G内存、10G硬盘、5M带宽&#xff0c;给人惊喜。三丰云免费服务器的性能稳定&#xff0c;让我可以尽情发挥技术的魔力。 Docker是一种轻量级容器技术&#xff0c;而Wordpress则是广受欢迎…

C++国密SM2算法加解密的使用

目录 效果 在线校验 代码实现参考 项目 下载 效果 加密字符串:lxw 123abcD 2024-09-01:12:00加密后信息:042E82EE8ACE2BD56FA71DC6A0C34190627AA365F8EEE6261903BEE327A85EB5E1D6E78F2D79AD6F6DC9E45C0829625DC3165BB78BD897F99044A640F930653747939CF9D5A10C8216F945A559…

【Leetcode 2357 】 使数组中所有元素都等于零 —— 哈希表

给你一个非负整数数组 nums 。在一步操作中&#xff0c;你必须&#xff1a; 选出一个正整数 x &#xff0c;x 需要小于或等于 nums 中 最小 的 非零 元素。nums 中的每个正整数都减去 x。 返回使 nums 中所有元素都等于 0 需要的 最少 操作数。 示例 1&#xff1a; 输入&am…

【手撕数据结构】二叉树oj题

目录 单值二叉树题目描述题目思路及代码 相同的树题目描述题目思路及代码 对称二叉树题目描述题目思路及代码 另一棵树的子树题目描述题目思路及代码 二叉树的前序遍历题目描述题目思路及代码 二叉树的构建与遍历题目描述题目思路及代码 单值二叉树 题目描述 题目思路及代码 …

10、Flink 动态表之表到流的转换详解

表到流的转换 动态表可以像普通数据库表一样通过 INSERT、UPDATE 和 DELETE 来不断修改,它可能是一个只有一行、不断更新的表,也可能是一个 insert-only 的表,没有 UPDATE 和 DELETE 修改,或者介于两者之间的其他表。 在将动态表转换为流或将其写入外部系统时,需要对这些…

JVM GC 调优

文章目录 引言I 调整JVM的默认堆内存配置1.1 java命令启动jar包时配置JVM 的内存参数1.2 基于Tomcat服务器部署的java应用,配置JVM 的内存参数II JVM GC 调优基本概念: 应用程序的响应时间(RT)和吞吐量(QPS)JVM调优原理调优思路调优方法JVM调优技巧建议引言 内存参数:ht…

为Ubuntu换颗“心”

对于现在的Linux发行版操作系统,都默认配置好相应的Kernel,但其版本远比最新的要旧,而最新的Kernel除了会修复已发现的BUG,有时还会更新部分框架以及新增功能模块代码,为了确保系统的稳定,还有体验下新功能,我们只好对操作系统的进行换“心”手术,这手术可不简单,首先…

Go 语言版本管理——Goenv

Go 语言版本管理——Goenv 命令安装 goenv安装和切换 Go 版本 goenv 是一个专门管理 Go 语言版本的工具。 命令 安装 goenv github-goenv git clone https://github.com/go-nv/goenv.git ~/.goenv echo export GOENV_ROOT"$HOME/.goenv" >> ~/.bash_profile…

字符编码简介

目录 1. ASCLL 2. GB2312 3. GBK/gbk 4. GB18030 5. Unicode 6. 总结 1. ASCLL 在计算机刚开始被美国人发明的时候&#xff0c;需要将字符存储到计算机进行运算或打印&#xff0c;于是选取了95 个可见字符&#xff08;数字0-9&#xff0c;英文字母&#xff0c;标点符号&…

超详细Git基本命令使用(二)

&#x1f600;前言 本篇博文是关于 Git基本命令的使用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f6…

SprinBoot+Vue实验室考勤管理小程序的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue3.6 uniapp代码 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平…

【0-1背包变种】力扣2787. 将一个数字表示成幂的和的方案数

给你两个 正 整数 n 和 x 。 请你返回将 n 表示成一些 互不相同 正整数的 x 次幂之和的方案数。换句话说&#xff0c;你需要返回互不相同整数 [n1, n2, …, nk] 的集合数目&#xff0c;满足 n n1x n2x … nkx 。 由于答案可能非常大&#xff0c;请你将它对 109 7 取余后…

深度学习100问35:如何避免梯度爆炸发生

嘿&#xff0c;想避免梯度爆炸这个麻烦家伙&#xff0c;有好多招儿呢。 首先说说权重初始化&#xff0c;这就好比给游戏里的角色分配初始能力值。得合理安排神经网络的权重初始化哦&#xff0c;不然一开始就可能出问题。可以用像 Xavier 初始化或者 He 初始化这些方法&#x…

Http的get请求中的URL中的占位符参数和查询参数有什么区别

Http的GET请求中的URL中的占位符参数和查询参数在功能、位置和用途上存在明显的区别。 占位符参数&#xff08;Path Variables&#xff09; 定义与位置&#xff1a;占位符参数是通过URL模板中的{}定义的&#xff0c;它们位于URL的路径&#xff08;path&#xff09;部分。例如…

Python实时聊天室架构与API实战应用

尊敬的各位读者&#xff0c;欢迎参与本次共享研讨项目——利用Python构建实时聊天室。在本项目中&#xff0c;我们将引进一款前沿工具——发布订阅频道API&#xff0c;以实现聊天室内的实时交互功能。 在当今信息泛滥的社会环境下&#xff0c;实时交流已成为人们日常生活中不可…

算法训练营|图论第7天 prim算法 kruskal算法

题目&#xff1a;prim算法 题目链接&#xff1a; 53. 寻宝&#xff08;第七期模拟笔试&#xff09; (kamacoder.com) 代码&#xff1a; #include<bits/stdc.h> #include<unordered_map> #include<unordered_set> using namespace std; int main() {int v…

人工智能 | 实现定制化 AutoGPT 实战

简介 在前面的学习过程中&#xff0c;已经了解到了 AutoGPT 基本的环境安装操作。接下来就可以基于 AutoGPT 完成一些有趣的任务。通过 AutoGPT 实现我们的需求 环境准备 在正式使用 AutoGPT 之前&#xff0c;确认以下环境没有任何问题&#xff1a; 稳定的科学上网环境。配…

国产芯片+国产操作系统打造办公系统

在《使用国产操作系统作为开发系统》一文中&#xff0c;我介绍了将开发系统从 Ubuntu 替换为 Deepin 系统的过程。经过一个多月的使用&#xff0c;Deepin 系统已然成为我的主力开发平台&#xff0c;其顺手程度让我对国产操作系统的信心大增。于是&#xff0c;我开始将目光瞄向公…