当前位置: 首页 > article >正文

别再死记硬背了!用Python和C++手写Dijkstra算法,搞懂路径规划核心(附完整代码)

从零实现Dijkstra算法Python与C双语言实战路径规划很多同学在刷算法题时都有这样的困惑看讲解视频时觉得思路清晰但自己动手写代码却无从下手。今天我们就用最直观的方式带你用Python和C两种语言完整实现Dijkstra算法彻底掌握路径规划的核心实现技巧。1. 为什么选择Dijkstra算法作为路径规划的起点路径规划算法种类繁多从A到RRT各有特点。但Dijkstra算法作为最基础的最短路径算法它的价值在于算法思想纯粹不依赖任何启发式函数纯粹基于贪心策略教学意义重大理解它能轻松过渡到A*等更复杂算法实际应用广泛从网络路由到游戏AI都有它的身影我第一次接触这个算法时最困惑的是如何将课本上的伪代码转化为实际可运行的代码。下面我们就从最基础的图表示方法开始一步步拆解实现细节。2. 图的表示邻接矩阵与邻接表的选择实现Dijkstra算法的第一步是如何在代码中表示图结构。常见的有两种方式2.1 邻接矩阵实现# Python邻接矩阵示例 INF float(inf) graph [ [0, 7, 9, INF, INF], [7, 0, 10, 15, INF], [9, 10, 0, 11, INF], [INF, 15, 11, 0, 6], [INF, INF, INF, 6, 0] ]对应的C实现// C邻接矩阵示例 const int INF INT_MAX; vectorvectorint graph { {0, 7, 9, INF, INF}, {7, 0, 10, 15, INF}, {9, 10, 0, 11, INF}, {INF, 15, 11, 0, 6}, {INF, INF, INF, 6, 0} };适用场景稠密图边数接近顶点数的平方时更节省空间2.2 邻接表实现# Python邻接表示例 from collections import defaultdict graph defaultdict(list) graph[0] [(1, 7), (2, 9)] graph[1] [(0, 7), (2, 10), (3, 15)] graph[2] [(0, 9), (1, 10), (3, 11)] graph[3] [(1, 15), (2, 11), (4, 6)] graph[4] [(3, 6)]对应的C实现// C邻接表示例 vectorvectorpairint, int graph { {{1, 7}, {2, 9}}, {{0, 7}, {2, 10}, {3, 15}}, {{0, 9}, {1, 10}, {3, 11}}, {{1, 15}, {2, 11}, {4, 6}}, {{3, 6}} };适用场景稀疏图边数远小于顶点数的平方时更高效实际项目中邻接表的使用频率更高因为它能更好地适应各种图结构。但在算法竞赛中邻接矩阵因其编码简单也常被采用。3. Dijkstra核心实现优先队列的妙用理解了图的表示方法后我们来看算法核心实现。关键在于如何高效地找到当前距离起点最近的节点。3.1 Python实现详解import heapq def dijkstra(graph, start): n len(graph) dist [float(inf)] * n dist[start] 0 heap [(0, start)] visited set() while heap: current_dist, u heapq.heappop(heap) if u in visited: continue visited.add(u) for v, weight in graph[u]: if dist[v] current_dist weight: dist[v] current_dist weight heapq.heappush(heap, (dist[v], v)) return dist关键点解析使用优先队列最小堆快速获取当前最小距离节点visited集合避免重复处理松弛操作Relaxation更新邻居节点距离3.2 C实现对比#include vector #include queue #include climits using namespace std; vectorint dijkstra(const vectorvectorpairint, int graph, int start) { int n graph.size(); vectorint dist(n, INT_MAX); dist[start] 0; priority_queuepairint, int, vectorpairint, int, greaterpairint, int pq; pq.push({0, start}); while (!pq.empty()) { auto [current_dist, u] pq.top(); pq.pop(); if (current_dist dist[u]) continue; for (const auto [v, weight] : graph[u]) { if (dist[v] dist[u] weight) { dist[v] dist[u] weight; pq.push({dist[v], v}); } } } return dist; }语言差异注意点C需要显式指定优先队列的比较函数C17结构化绑定简化了pair的访问需要包含额外头文件4. 路径回溯记录完整路径而不仅是距离实际应用中我们不仅需要知道最短距离还需要知道具体路径。下面我们扩展实现来记录路径。4.1 Python路径回溯实现def dijkstra_with_path(graph, start): n len(graph) dist [float(inf)] * n dist[start] 0 prev [-1] * n # 记录前驱节点 heap [(0, start)] while heap: current_dist, u heapq.heappop(heap) if current_dist dist[u]: continue for v, weight in graph[u]: if dist[v] dist[u] weight: dist[v] dist[u] weight prev[v] u heapq.heappush(heap, (dist[v], v)) # 构建路径 paths [] for i in range(n): path [] current i while current ! -1: path.append(current) current prev[current] paths.append(path[::-1]) return dist, paths4.2 C路径回溯实现pairvectorint, vectorvectorint dijkstraWithPath(const vectorvectorpairint, int graph, int start) { int n graph.size(); vectorint dist(n, INT_MAX); vectorint prev(n, -1); dist[start] 0; priority_queuepairint, int, vectorpairint, int, greaterpairint, int pq; pq.push({0, start}); while (!pq.empty()) { auto [current_dist, u] pq.top(); pq.pop(); if (current_dist dist[u]) continue; for (const auto [v, weight] : graph[u]) { if (dist[v] dist[u] weight) { dist[v] dist[u] weight; prev[v] u; pq.push({dist[v], v}); } } } // 构建路径 vectorvectorint paths(n); for (int i 0; i n; i) { int current i; while (current ! -1) { paths[i].push_back(current); current prev[current]; } reverse(paths[i].begin(), paths[i].end()); } return {dist, paths}; }5. 性能优化与常见问题排查实现基本功能后我们还需要关注一些优化技巧和常见陷阱。5.1 时间复杂度对比实现方式时间复杂度适用场景邻接矩阵普通队列O(V²)小规模图邻接表优先队列O(E VlogV)稀疏图斐波那契堆O(E VlogV)理论最优但实现复杂5.2 常见错误排查负权边问题Dijkstra不能处理负权边遇到负权边应考虑Bellman-Ford算法堆优化陷阱# 错误示例直接修改堆中元素 heapq.heappush(heap, (new_dist, v)) # 正确做法 # 错误试图直接修改堆中已有元素的优先级初始化问题// 必须初始化所有距离为INF vectorint dist(n, INT_MAX); dist[start] 0; // 起点设为0大数溢出INF float(inf) # Python可用 // C中建议使用INT_MAX/2防止加法溢出 const int INF 0x3f3f3f3f; // 约1e9常用技巧5.3 实际应用中的优化技巧双向Dijkstra同时从起点和终点开始搜索相遇时停止A*算法加入启发式函数适用于已知目标位置的场景预处理技术如Contraction Hierarchies加速多次查询# 双向Dijkstra示例框架 def bidirectional_dijkstra(graph, start, end): # 初始化前向和后向搜索 forward_dist {start: 0} backward_dist {end: 0} forward_heap [(0, start)] backward_heap [(0, end)] best_dist float(inf) meeting_node None while forward_heap and backward_heap: # 前向搜索一步 if forward_heap: current_dist, u heapq.heappop(forward_heap) if u in backward_dist: total current_dist backward_dist[u] if total best_dist: best_dist total meeting_node u # 后向搜索一步 if backward_heap: current_dist, u heapq.heappop(backward_heap) if u in forward_dist: total current_dist forward_dist[u] if total best_dist: best_dist total meeting_node u # 合并路径 if meeting_node is not None: # 合并前向和后向路径 return best_dist return float(inf)6. 实战测试从算法题到实际应用为了验证我们的实现让我们用几个经典案例进行测试。6.1 LeetCode案例测试743. 网络延迟时间标准的Dijkstra应用场景def networkDelayTime(times, n, k): graph defaultdict(list) for u, v, w in times: graph[u-1].append((v-1, w)) dist dijkstra(graph, k-1) max_dist max(dist) return max_dist if max_dist float(inf) else -16.2 实际应用场景假设我们要为一个物流系统设计路径规划模块struct DeliveryPoint { int id; double latitude; double longitude; }; vectorint findOptimalRoute(const vectorDeliveryPoint points, const vectortupleint, int, int roads, int depotIndex) { // 构建图 vectorvectorpairint, int graph(points.size()); for (const auto [u, v, w] : roads) { graph[u].emplace_back(v, w); graph[v].emplace_back(u, w); // 无向图 } // 计算最短路径 auto [dist, paths] dijkstraWithPath(graph, depotIndex); // 找出最优配送顺序简化版 vectorint deliveryOrder(points.size()); iota(deliveryOrder.begin(), deliveryOrder.end(), 0); sort(deliveryOrder.begin(), deliveryOrder.end(), [dist](int a, int b) { return dist[a] dist[b]; }); return deliveryOrder; }7. 扩展思考Dijkstra的变种与应用掌握了基础实现后我们可以进一步探索一些有趣的变种多目标Dijkstra同时计算到多个目标点的最短路径时间依赖Dijkstra考虑边权重随时间变化的情况概率Dijkstra考虑边存在概率的情况# 多目标Dijkstra示例 def multi_target_dijkstra(graph, start, targets): n len(graph) dist [float(inf)] * n dist[start] 0 heap [(0, start)] remaining_targets set(targets) results {} while heap and remaining_targets: current_dist, u heapq.heappop(heap) if u in remaining_targets: results[u] current_dist remaining_targets.remove(u) for v, weight in graph[u]: if dist[v] current_dist weight: dist[v] current_dist weight heapq.heappush(heap, (dist[v], v)) return results在自动驾驶项目中我们曾遇到一个有趣的问题如何在动态变化的交通环境中实时更新最短路径。传统的Dijkstra每次都要重新计算效率太低。最终我们采用了增量式更新的方案只在道路权重变化时重新计算受影响的部分路径。

相关文章:

别再死记硬背了!用Python和C++手写Dijkstra算法,搞懂路径规划核心(附完整代码)

从零实现Dijkstra算法:Python与C双语言实战路径规划 很多同学在刷算法题时都有这样的困惑:看讲解视频时觉得思路清晰,但自己动手写代码却无从下手。今天我们就用最直观的方式,带你用Python和C两种语言完整实现Dijkstra算法&#x…...

ESP32+MicroPython玩转ST7735小屏幕:从接线到显示中文的保姆级避坑指南

ESP32MicroPython玩转ST7735小屏幕:从接线到显示中文的保姆级避坑指南 1. 硬件准备与接线图解析 当你第一次拿到ESP32开发板和ST7735屏幕时,面对密密麻麻的引脚可能会感到无从下手。别担心,我们先从最基础的物理连接开始。ESP32的3.3V逻辑电平…...

从Pikachu靶场实战出发:用Python脚本自动化搞定SQL盲注(布尔/时间)

从Pikachu靶场实战出发:用Python脚本自动化搞定SQL盲注(布尔/时间) 在渗透测试的世界里,SQL盲注就像一场与数据库的无声对话——你看不到错误信息,只能通过微妙的真假响应或时间延迟来推断数据。Pikachu靶场作为经典的…...

从D3 0_到MSM:RTCM3.2协议帧结构深度解析与实战解码

1. RTCM3.2协议入门:从"D3 0_"开始的导航数据之旅 第一次看到RTCM3.2数据流时,那串以"D3 0_"开头的十六进制代码让我完全摸不着头脑。就像面对一本用外星语言写成的密码本,每个字节都像是在嘲笑我的无知。但当我真正理解…...

告别命令行!用Kafka Tool 2.0.4图形化界面管理Topic和消息的保姆级教程

告别命令行!用Kafka Tool 2.0.4图形化界面管理Topic和消息的保姆级教程 你是否曾在深夜对着黑底白字的Kafka命令行界面抓狂?或是面对kafka-topics.sh和kafka-console-consumer.sh的复杂参数感到迷茫?今天,我们将彻底解放你的双手…...

MAX30102数据飘、读数不准?手把手教你调试与滤波实战(STM32平台)

MAX30102数据飘、读数不准?手把手教你调试与滤波实战(STM32平台) 当你在STM32平台上使用MAX30102进行心率血氧监测时,是否遇到过数据波动大、读数不稳定的问题?这可能是硬件设计、环境干扰或软件处理等多方面因素共同作…...

WarcraftHelper:魔兽争霸3在现代系统上的终极兼容性修复工具

WarcraftHelper:魔兽争霸3在现代系统上的终极兼容性修复工具 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸3在现代电脑上…...

鸿蒙ArkTS性能不够用?试试用Rust写个‘外挂’:手把手教你集成NAPI模块提升计算效率

鸿蒙ArkTS性能优化实战:用Rust打造高性能NAPI模块 ArkTS作为鸿蒙生态的主力开发语言,在UI构建和业务逻辑处理上表现出色,但遇到复杂计算任务时,性能瓶颈往往成为开发者的痛点。本文将带你深入探索如何通过Rust编写NAPI原生模块&am…...

SuperMap GIS处理BIM数据避坑指南:从模型检查到缓存生成的12个常见误区

SuperMap GIS处理BIM数据避坑指南:从模型检查到缓存生成的12个常见误区 在建筑信息模型(BIM)与地理信息系统(GIS)融合应用的实践中,许多工程师都会遇到这样的困惑:明明按照标准流程操作&#xf…...

告别云端:5步在本地用Orthanc搭建轻量级DICOM影像服务器,管理你的CT/MRI数据集

告别云端:5步在本地用Orthanc搭建轻量级DICOM影像服务器,管理你的CT/MRI数据集 医学影像数据的管理一直是临床医生和科研人员面临的挑战。想象一下,当你需要快速调取某个患者的CT序列进行多学科会诊,或是需要批量处理数千张MRI图…...

GLPI安装总报错?这份CentOS 7下的“保姆级”排错指南请收好(附PHP模块、文件权限详解)

GLPI安装总报错?这份CentOS 7下的“保姆级”排错指南请收好(附PHP模块、文件权限详解) 在CentOS 7上部署GLPI时,即使按照教程一步步操作,也常常会遇到各种"坑"。本文将带你深入排查这些常见问题,…...

别再纠结了!FLUENT两相流VOF、Mixture、Eulerian模型到底怎么选?附实战场景对比

FLUENT两相流模型实战指南:VOF、Mixture与Eulerian的精准选择策略 在计算流体动力学(CFD)领域,两相流问题一直是工程师们面临的重要挑战。无论是化工反应器中的气液混合,还是石油管道中的油水分离,亦或是能…...

手把手教你用Skyline健康检查辅助VSAN集群安全关机(附7.0U3新功能解读)

深度解析:如何利用健康检查工具优化VSAN集群安全关机流程 1. 为什么VSAN集群关机需要特殊流程? 虚拟化环境中的存储集群关机从来都不是简单的"点一下关机按钮"就能完成的操作。VSAN作为VMware的软件定义存储解决方案,其分布式特性使…...

RK3588双系统实战:从分区表设计到fstab修改,手把手教你构建Android 12与Linux Debian共存环境

RK3588双系统深度实践:Android 12与Debian的精密共存架构设计 当工业级设备需要同时承载高性能图形交互与稳定后台服务时,RK3588的双系统架构展现出独特价值。想象一下,一台医疗影像终端既能运行Android的触控应用,又能通过Linux …...

告别屏幕偏色!用高通QDCM 6.0 + CA-410为你的安卓设备做一次专业级色彩校准

高通QDCM 6.0与CA-410联袂:解锁安卓设备专业级色彩校准全流程 当你在不同设备上查看同一张照片时,是否发现色彩表现天差地别?专业设计师的作品在手机上显示偏黄,视频创作者的内容在平板上泛青——这些恼人的色差问题,根…...

避坑指南:PyTorch F.interpolate里align_corners参数到底怎么设?附对比图

深度解析PyTorch插值操作:align_corners参数实战指南 在计算机视觉和深度学习领域,张量的空间维度变换是最基础却最容易出错的环节之一。许多开发者在初次接触PyTorch的F.interpolate函数时,往往会被align_corners这个看似简单的布尔参数困扰…...

为什么Adobe GenP 3.0成为创意工作者的数字工具箱钥匙?

为什么Adobe GenP 3.0成为创意工作者的数字工具箱钥匙? 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP 在数字创意领域,Adobe Creative Clou…...

别再只调SystemClock_Config!深入HC32F460时钟树,搞懂HRC、XTAL和PLL的切换逻辑

深入HC32F460时钟树:从HRC到PLL的动态切换实战指南 在嵌入式开发中,时钟系统如同芯片的"心跳",决定了整个系统的运行节奏。HC32F460作为一款高性能MCU,其时钟架构设计既灵活又复杂,许多开发者往往止步于复制…...

告别内核打印!用devmem2在嵌入式Linux上直接读写寄存器(附交叉编译踩坑实录)

嵌入式Linux寄存器调试利器:devmem2实战指南与交叉编译全解析 调试嵌入式系统时,最令人头疼的莫过于反复修改内核驱动、重新编译、烧录镜像的漫长循环。想象一下这样的场景:你正在调试一块全新的ARM开发板,GPIO死活不工作&#x…...

告别网盘限速!八大网盘直链下载助手完整使用指南

告别网盘限速!八大网盘直链下载助手完整使用指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 …...

保姆级教程:不用修改标准表,如何优雅地增强SAP MD11/MD12/MD13屏幕字段?

SAP MD11/MD12/MD13屏幕增强:无侵入式开发的优雅实践 在SAP项目实施过程中,业务需求的变更往往要求对标准事务码进行界面调整,而MD11/MD12/MD13这类核心计划订单事务的屏幕增强尤为常见。传统做法直接修改标准表结构或覆盖标准程序&#xff0…...

Python自动化控制Comsol多物理场仿真的完整指南:MPh库实战解析

Python自动化控制Comsol多物理场仿真的完整指南:MPh库实战解析 【免费下载链接】MPh Pythonic scripting interface for Comsol Multiphysics 项目地址: https://gitcode.com/gh_mirrors/mp/MPh 想要用Python代码自动化控制Comsol多物理场仿真吗?…...

华为AR路由器Console密码忘了别慌,BootROM菜单里这个选项能一键清空(附不同版本默认密码)

华为AR路由器Console密码恢复实战指南:BootROM密码管理功能详解 凌晨三点,机房告警灯突然亮起,核心业务中断。当你火速赶到现场,却发现那台关键华为AR路由器的Console密码怎么输都不对——这种场景恐怕是每位网络工程师的噩梦。别…...

VSCode Clangd插件配置避坑指南:解决Linux内核代码跳转失效和‘bear make’的那些坑

VSCode Clangd插件深度调优:Linux内核开发者的高效导航实战 当你面对数百万行的Linux内核源码时,代码跳转和智能补全不再是奢侈品,而是生产力刚需。作为嵌入式开发老手,我经历过无数次Clangd配置失败后的挫败感——那些看似简单的…...

3分钟快速上手:FigmaCN中文界面插件的终极指南

3分钟快速上手:FigmaCN中文界面插件的终极指南 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 你是否曾在使用Figma时因为英文界面而感到困扰?面对"Frame&q…...

开源工具douyin-downloader:破解抖音内容保存难题的技术方案与实践指南

开源工具douyin-downloader:破解抖音内容保存难题的技术方案与实践指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browse…...

AI写专著必备!一键生成20万字专著,AI专著生成工具助你高效写作!

创新是学术专著的关键所在,同时也是写作上的一大挑战。一部优秀的专著,不应该仅仅是对已有研究的汇集,而是必须要有贯穿整本书的独特观点、理论框架或者新的研究方法。在浩如烟海的学术资料面前,挖掘出未被研究的领域并不容易——…...

别再只会load(‘data.mat‘)了!Matlab数据加载的5个隐藏技巧与实战避坑

别再只会load(data.mat)了!Matlab数据加载的5个隐藏技巧与实战避坑 每次看到同事在Matlab里反复输入load(data.mat)时,我都忍不住想冲过去分享几个能节省半小时的冷门技巧。作为从学生时代就被Matlab"折磨"过来的老用户,我踩过的坑…...

如何做好测试?(八)兼容性测试实战:从策略到工具的完整落地指南

1. 兼容性测试的核心价值与挑战 兼容性测试就像给软件做"体检",确保它在各种环境下都能健康运行。想象一下,你开发了一个精美的电商网站,在Chrome上运行完美,结果用户用Safari打开发现购物车按钮消失了——这种问题轻则…...

从CAD转战CREO?这份高效上手攻略帮你快速打通草绘、零件与工程图核心模块

从CAD转战CREO:参数化设计思维与核心模块高效迁移指南 如果你已经熟练使用SolidWorks、AutoCAD或UG/NX等CAD软件,初次接触CREO时可能会感到困惑——为什么绘制一个简单矩形需要先草绘轮廓再标注尺寸?为什么修改模型参数会自动更新所有关联视图…...