正权图的 Dijkstra 最短路算法

news/2025/2/19 12:54:35/文章来源:https://www.cnblogs.com/ofnoname/p/18717583

最短路径问题是图论中最经典且重要的应用问题之一。它的目标是找到一个图中从起点到终点的最短路径,即在所有可能的路径中,选择一条边权和最小的路径。该问题广泛存在于多个实际场景中,比如交通运输、通信网络、导航系统等。

在实际生活中,很多情况都涉及到寻找最短路径。例如,导航系统需要为用户推荐从当前位置到目的地的最短路线,或者在计算机网络中,数据包可能需要找到从源点到目标节点的最短传输路径。此外,物流公司在进行货物配送时,也会使用最短路径算法来最优化运输路线,减少成本和时间。

最短路径问题可以分为几类,主要区别在于图中边的权重特性。这里,我们关心的是“正权图”和“负权图”的区别:

  • 正权图:在正权图中,所有边的权重都为正数。这类图广泛应用于实际生活中的路径规划问题,因为权重通常代表着距离、时间、费用等,通常都是正数。

  • 负权图:与正权图不同,负权图允许边的权重为负数。负权边的存在可能导致问题变得复杂,因为如果某些路径经过负权边,可能会导致最短路径无限循环下降,无法收敛。因此,解决带有负权边的图时需要使用不同的算法,如 Bellman-Ford 算法。

在最短路问题上,正权图和负权图有着质的区别。本文将聚焦于正权图,并探讨 Dijkstra 算法在正权图中的应用。

Dijkstra 算法

Dijkstra 算法是一种典型的贪心算法,旨在解决单源最短路径问题。它的核心思想是每次选择当前距离起点最近的未访问节点,并逐步更新与其邻接的节点的最短路径。Dijkstra算法通过逐渐扩展已知的最短路径集合,最终得到从起点到所有节点的最短路径。

Dijkstra算法的执行步骤如下:

  1. 初始化:设定起点的距离为0,其他所有节点的距离为无穷大。将所有点划分为“已求解最短距离”和“未求解最短距离”两部分。开始起点的最短路径已知,其它节点的最短路径未知。

    \[\text{dist}(s) = 0, \quad \text{dist}(v) = \infty \quad \text{for all other nodes } v \neq s \]

  2. 选择当前最近节点:从未访问的节点中选择距离起点最近的节点,记为当前节点 \(u\)

  3. 更新邻接节点的距离:对于当前节点 \(u\) 的所有邻接节点 \(v\),如果通过 \(u\)\(v\) 的路径比已知的最短路径更短,则更新 \(v\) 的最短路径:

    \[\text{dist}(v) = \min(\text{dist}(v), \text{dist}(u) + w(u, v)) \]

    其中 \(w(u, v)\) 表示边 \(u \to v\) 的权重。

  4. 标记当前节点已访问:将当前节点 \(u\) 标记为已访问,不再参与后续的最短路径计算。

  5. 重复上述过程:重复步骤 2 到步骤 4,直到所有节点都被访问过,或者起点到所有其他节点的最短路径已经找到。

Dijkstra 算法的正确性

Dijkstra 算法的正确性可以通过“贪心选择性质”和“最短路径定理”来证明。

  • 贪心选择性质:对于任何一个顶点 \(v\),如果已知通过某个节点 \(u\)\(v\) 的路径是当前最短的路径,那么从起点到 \(v\) 的最短路径一定是通过 \(u\)\(v\) 的路径。也就是说,Dijkstra算法每次选择距离起点最近的未访问顶点 \(u\),这个选择是局部最优的,从而保证了全局最优。

  • 最短路径定理:如果一个路径是从源点到目标节点的最短路径,那么在路径的任意前缀段(从起点到某个中间节点的路径)也是最短路径。这个定理保证了算法每次扩展的路径是最短的。

基于这些性质,Dijkstra算法能够在每次迭代中得到当前节点的最短路径,最终保证算法得到从源点到所有节点的最短路径。

在正权图中,每一次选择当前最短节点的操作是有效的,那是因为每个新选择的节点都不会影响已确定最短路径的正确性。但在负权图中,负权边可能导致我们先前认为最短的路径变得不再最短。因此,如果图中包含负权边,Dijkstra 算法无法正确地更新路径,也就无法确保最终结果是全局最优的。

// 二叉堆版本
vector<long long> dijkstra(int s) {vector<long long> dist(n, LLONG_MAX / 2);vector<bool> visited(n, false);dist[s] = 0;priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;pq.push({0, s});while (!pq.empty()) {int u = pq.top().second;pq.pop();if (visited[u]) continue;visited[u] = true;for (const auto &e : adj[u]) {int v = e.to;int w = e.weight;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;pq.push({dist[v], v});}}} return dist;
}

时间复杂度分析

Dijkstra 算法的时间复杂度依赖于其实现方式,主要影响因素是如何选择距离起点最近的未访问节点。我们将分析不同的数据结构实现下的时间复杂度。

  1. 暴力方法(无堆实现)

    在暴力方法中,我们每次从未访问的节点中选择距离起点最近的节点,通常通过遍历所有未访问节点来实现。这种方法的时间复杂度为 \(O(V^2)\),其中 \(V\) 是图中节点的数量。每次选择最短路径节点需要 \(O(V)\) 时间,更新邻接节点的距离需要 \(O(E)\) 时间(\(E\) 为边数),总的时间复杂度为 \(O(V^2)\)

  2. 使用二叉堆优化

    通过使用二叉堆(最小堆)来维护未访问节点的最短距离,我们可以在每次选择最短路径节点时做到 \(O(\log V)\) 时间复杂度。对于每条边的更新操作,也需要 \(O(\log V)\) 时间。因此,使用二叉堆后,Dijkstra算法的时间复杂度为 \(O((V + E) \log V)\)

  3. 使用 k 叉堆优化

    使用 \(k\) 叉堆时,每次选择最短路径节点的时间复杂度为 \(O(\log_k V)\),而每条边的更新操作仍然是 \(O(\log_k V)\)。对于一般情况,\(k\) 的取值在实践中可能比二叉堆略大,对于非常稠密的图,多叉堆比二叉堆更好。总体时间复杂度为 \(O((V + E) \log_k V)\)

  4. 使用斐波那契堆优化

    斐波那契堆是一个更高级的堆结构,更新操作(减小堆中元素的值)的摊销时间复杂度为 \(O(1)\)。因此,使用斐波那契堆的时间复杂度为 \(O(E + V \log V)\),这是理论的最优实现。

总结来说,Dijkstra算法的时间复杂度在不同的数据结构下有显著的差异,具体选择哪种结构取决于图的特性和实际需求。

Dijkstra 算法是正权图的最佳选择

Dijkstra 算法在正权图中有着显著的优势,适用于有向图,也适用于无向图。稳定且效率高。但其无法处理负权图。Bellman-Ford 算法才能够处理负权边并检测负权环。

对于全源最短路径, Floyd-Warshall 算法是一个用于求解所有顶点对之间最短路径的算法,其时间复杂度为 \(O(V^3)\)。虽然该算法适用于所有类型的图,但对于大规模图,尤其是图中边数较少时,Floyd-Warshall 算法的计算成本远高于 Dijkstra 算法。Dijkstra 算法更加高效,我们可以直接分别以每个点作为源点运行一次算法,得到全源最短路径。

A* 算法是对 Dijkstra 算法的一种优化,通常用于有启发式信息的路径搜索问题。A* 算法通过加入启发式函数来引导搜索,使得算法在求解最短路径时更加高效,尤其在路径搜索中与目标节点的距离较近时。若非单纯的图论求解,而在某些存在启发式的场景下(如现实地图,坐标系中的点等),可以在 dijkstra 中引入启发式。

当边权均为 1 时,我们发现 dijstra 退化为 BFS,不再需要优先队列了。反过来说,dijstra 就是 BFS 在边权不等需要用优先队列先取出最小值的扩展。

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

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

相关文章

(自适应手机端)英文外贸网站模板 日用百货网站源码下载

(自适应手机端)英文外贸网站模板 日用百货网站源码下载 PbootCMS内核开发的网站模板,该模板适用于外贸网站模板、日用百货网站源码等企业,当然其他行业也可以做,只需要把文字图片换成其他行业的即可; 自适应移动端,同一个后台,数据即时同步,简单适用!附带测试数据!友好…

Markdown 语法学习

Markdown 语法学习 标题: # + 三级标题 四级标题 字体 hello,world! hello,world! hello,world! hello,world! 引用选择狂神说Java>这是引用分割线图片超链接 博客园 - 开发者的网上家园) 列表A B CA B C表格名字 性别 生日张三 男 1997.1.1李斯 女 1995.7.6代码hello

一级倒立摆平衡控制系统MATLAB仿真,可显示倒立摆平衡动画,对比极点配置,线性二次型,PID,PI及PD五种算法

1.算法仿真效果 本课题是针对博主原来写的文章《基于MATLAB的一级倒立摆控制仿真,带GUI界面操作显示倒立摆动画,控制器控制输出》的升级。升级内容包括如下几个方面:增加了PI控制器,PD控制器,同时对极点配置,线性二次型,PID,PI及PD五种算法的控制输出曲线进行对比。matlab2…

使用Spring Initializr方式构建Spring Boot项目

按教材 下例选用IntelliJ IDEA 2024.3.3付费版 创建项目JDK版本根据实际情况。启动类简析 package com.example.demo; // 这段代码位于 com.example.demo 这个包下import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBo…

未注册类。WSL 安装似乎已损坏。WslRegisterDistribution failed with error: 0x8007019e/

前言 不知何时,我的wsl环境又坏了。 其实wsl我不怎么用,但是今天突然想用一下docker,但是没有启动成功。 于是乎我试了一下wsl,发现是wsl完全坏了。 之前卸载过wsl的某个子系统,比如ubuntu22,当初想着换24,之后很长一段时间没用过wsl。 尽管我怀疑是这个操作造成的问题,…

清华大学最新力作,DeepSeek 从入门到精通PDF

前几天,我发布了一篇关于 WPS 集成 DeepSeek 的教程,没想到大家的反响非常热烈!很多朋友都表示,通过这篇教程学到了不少关于 DeepSeek 的实用技巧,赶紧点进来看。不过,今天我给大家带来了更猛的干货——清华大学最新发布的 DeepSeek 资料!而且这次的资料更加关注 DeepSe…

安装程序无法自动安装virtual machine

安装VMware Tools 失败 发现在给过旧的系统安装 安装VMware Tools 的时候,会失败,比如 win7、win server2008等。原因 官网的说明,在这里 看不懂的自行翻译: 微软已将 Windows 驱动程序签名更改为自 2019 年 12 月 3 日起仅使用 SHA-2 算法,不支持 SHA-2 代码签名的旧版 W…

尝试在 Kubernetes 集群上用阿里云 GPU 实例部署 Ollama + DeekSeek-R1

在前一篇博文中尝试了使用阿里云 GPU 实例 ECS 单机部署了 DeekSeek-R1 7b 模型 。这篇博文记录一下在 k8s 集群上用阿里云 GPU 实例部署 DeekSeek R1 大模型的尝试过程,部署的也是 deepseek-r1:7b 模型。 先准备一台阿里云 GPU 实例,选用了共享型 GPU 实例 sgn7i,8核31G,4…

基于SOA海鸥优化算法的三维曲面最高点搜索matlab仿真

1.程序功能描述 基于SOA海鸥优化算法的三维曲面最高点搜索matlab仿真,输出收敛曲线以及三维曲面最高点搜索结果。 2.测试软件版本以及运行结果展示MATLAB2022A版本运行 3.核心程序for ij = 1:Mgen% 初始化本代最佳适应度及对应解yfit = 0.0;xbest = zeros(1,2); % 初…

Tomasulo 调度算法实现架构分析

虽然早在 1967 年就已经提出了 Tomasulo 调度算法 [1],但网上仍很少找到关于落到模块粒度的教程文档。从零复现一遍成本太大,因此用画原理图的方式做思想实验,尝试理解 Tomasulo 在电路上如何实现。 处理数据 首先明细几个概念指令(Instruction):包含指令类型(ADD、MUL、…

最新VMware Workstation虚拟机下载并安装

[2025年]最新VMware Workstation虚拟机下载并安装 1.最新VMware Workstation下载地址 2024年5月5日之后,VMware workstation不能直接在vmware官网进行下载了,需要在broadcom博通网站上进行下载 博通收购Vmware后(将workstation免费了),如下 旧的地址点击下载的时候会跳转到…

js之prototype

var TestPrototype = function () {this.propA = 1;this.methodA = function() {return this.propA;} }TestPrototype.prototype = {methodB: function() {return this.propA;} }var objA = new TestPrototype();objA.methodA() // 1 objA.methodB() // 1JavaScript 中, 任何函…