尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

最短路径算法全解析:从Dijkstra到Bellman-Ford的MATLAB建模实战

最短路径算法全解析:从Dijkstra到Bellman-Ford的MATLAB建模实战 1. 从地图导航到网络路由为什么最短路径问题无处不在我们每天都在和“最短路径”打交道只是很多时候没有意识到。早上打开手机地图输入公司和家的地址App瞬间为你规划出一条耗时最短或距离最短的路线——这背后就是最短路径算法在默默工作。快递公司如何规划配送路线让快递员在一天内送完尽可能多的包裹通信网络中的一个数据包如何选择路径才能最快到达目的地甚至在社交网络中如何计算两个人之间的“六度分隔”关系这些看似不同领域的问题其核心数学模型都可以归结为“图论中的最短路径问题”。简单来说图论就是把复杂的关系网络抽象成“点”和“边”。点可以代表城市、路由器、社交网络中的用户边则代表连接它们的道路、光纤、好友关系。而每条边通常会有一个“权重”比如距离、时间、成本。最短路径问题的目标就是在这样的一个图中找到从一个起点到一个终点所经过边的权重之和最小的那条路径。对于数学建模竞赛的参赛者而言最短路径问题几乎是必考题之一。无论是2014年“嫦娥三号”软着陆轨道设计中的最优控制问题本质是连续空间的最优路径还是历年赛题中涉及到的交通调度、网络优化、灾害救援路径规划都离不开对最短路径思想的深刻理解和灵活应用。很多同学初次接触时会觉得迪杰斯特拉Dijkstra、贝尔曼-福特Bellman-Ford这些算法名字很唬人代码实现也无从下手。但事实上一旦你理解了它们背后的核心思想——就像理解了不同工具的使用场景——你就能在面对千变万化的赛题时迅速找到那把最合适的“钥匙”。本文将从一个建模者的实战视角彻底拆解最短路径问题。我不会只给你枯燥的算法步骤而是会结合MATLAB这一数学建模“神器”带你弄懂为什么迪杰斯特拉算法不能处理负权边贝尔曼-福特算法多出来的那一重循环到底在干什么面对不同的赛题场景如动态交通、多目标优化我们该如何对经典算法进行改造和扩展我会分享我在实际培训和参赛中总结的代码模板、调试技巧和避坑指南让你不仅能“看懂”更能“上手用”在下次遇到相关问题时能够自信地写出高效、可靠的解决方案。2. 图的表示与建模将实际问题转化为算法可解的模型在调用任何炫酷的算法之前最基础也最关键的一步是如何把你的赛题描述翻译成计算机能理解的“图”。这一步做错了后面算法再精妙也是徒劳。建模的本质是抽象和简化我们需要抓住问题的主要矛盾忽略次要细节。2.1 邻接矩阵最直观的“关系表”对于节点数不是特别多比如几百个以内的图邻接矩阵是最常用的表示方法。它是一个n x n的矩阵n为节点数其中A(i, j)的值表示从节点i到节点j的边的权重。关键决策权重与“不可达”的表示权重含义根据问题决定。可能是物理距离、通行时间、经济成本、风险系数等。例如在物流配送中成本可能包含距离油耗和过路费你需要将其统一量纲后相加作为边的权重。“不可达”或“无连接”这是最容易出错的地方。在MATLAB中实现最短路径算法时我们通常用Inf无穷大来表示两个节点之间没有直接的边相连。绝对不要用0因为0权重意味着“免费通行”这与“无法通行”是天壤之别。假设我们有4个城市节点1-4道路边情况如下1到2距离101到3距离32到4距离23到2距离43到4距离8。2到3没有直接道路。我们可以构建邻接矩阵n 4; % 4个节点 A inf(n); % 初始化为全Inf矩阵 A(1,1)0; A(2,2)0; A(3,3)0; A(4,4)0; % 自己到自己的距离为0非必须但更严谨 A(1,2) 10; A(1,3) 3; A(2,4) 2; A(3,2) 4; A(3,4) 8; % 注意这是一个有向图的例子。如果道路是双向的无向图则需要对称赋值 % A(2,1) 10; A(3,1) 3; 等等。一个实用的建模技巧很多赛题数据是表格形式的比如“起点终点距离”。你可以用循环快速构建邻接矩阵data [1,2,10; 1,3,3; 2,4,2; 3,2,4; 3,4,8]; % 每一行是一条边 A inf(n); for i 1:size(data,1) A(data(i,1), data(i,2)) data(i,3); end2.2 邻接表应对大规模稀疏图的利器当图的节点数成千上万而每个节点只与少数几个其他节点相连时例如社交网络、某些道路网邻接矩阵会变得非常稀疏浪费大量内存存储无数个Inf。这时就该使用邻接表。在MATLAB中我们可以用元胞数组cell array来实现邻接表。adjList{i}存储一个列表列出所有从节点i出发能到达的邻居节点以及对应的边权重。n 4; adjList cell(n, 1); % 创建n行的元胞数组 adjList{1} [2, 10; 3, 3]; % 节点1连接到节点2权重10和节点3权重3 adjList{2} [4, 2]; adjList{3} [2, 4; 4, 8]; adjList{4} []; % 节点4没有出去的边邻接表节省空间但在查询“节点i到节点j是否有边”时需要遍历adjList{i}速度比邻接矩阵的直接索引A(i,j)慢。因此选择哪种表示法取决于你的图规模和算法需求。对于经典的迪杰斯特拉算法使用邻接表配合优先队列或最小堆是效率更高的标准实现方式不过MATLAB内置的graph和digraph对象已经帮我们优化了底层存储。2.3 使用MATLAB内置图对象事半功倍的选择从R2015b开始MATLAB引入了强大的graph无向图和digraph有向图对象。对于数学建模我强烈推荐直接使用它们而不是手动管理矩阵或元胞数组。它语法简洁内置了丰富的图算法和可视化功能。% 创建有向图 s [1 1 2 3 3]; % 起点节点数组 t [2 3 4 2 4]; % 终点节点数组 w [10 3 2 4 8]; % 权重数组 G digraph(s, t, w); % 创建无向图边是双向的 % G graph(s, t, w); % 查看图的基本信息 disp(G) % 可视化小规模图调试时非常有用 plot(G, EdgeLabel, G.Edges.Weight, LineWidth, 2, MarkerSize, 7); title(道路网络图);使用内置图对象后求最短路径就变得异常简单[path, dist] shortestpath(G, startNode, endNode, Method, positive)。这里的positive方法默认使用类似迪杰斯特拉的算法。我们后续要深入学习的就是这些内置函数背后的原理以及当内置函数不满足我们特殊需求时比如需要记录多条最短路径、处理动态权重如何自己动手实现。注意在建模比赛中虽然直接调用shortestpath函数很快但你必须要在论文中阐明所使用的算法原理。评委希望看到的是你理解了方法而不是仅仅会调用工具箱。自己实现一遍核心算法是理解它的最佳途径。3. 迪杰斯特拉算法步步为营的“贪心”搜索迪杰斯特拉算法是解决非负权重图单源最短路径问题最著名、最有效的算法之一。它的核心思想是一种“贪心”策略每次从未确定最短路径的节点中选择一个距离起点最近的节点认为它的当前距离就是最终最短距离然后通过它来更新其邻居节点的距离。3.1 算法核心思想与手动模拟让我们用之前的4城市例子手动模拟从城市1出发到其他所有城市的最短路径。初始化dist: 记录从起点到每个节点的当前已知最短距离。dist(1)0,dist(2)inf,dist(3)inf,dist(4)inf。visited: 标记节点是否已确定最短路径。全部初始化为false。prev: 记录到达每个节点的前驱节点用于最后回溯路径。初始化为-1或0。迭代过程所有未访问节点中dist最小的是节点1距离0。标记节点1为已访问。检查节点1的所有邻居节点2和节点3。对于节点2新距离 dist(1) A(1,2) 01010。小于当前inf更新dist(2)10,prev(2)1。对于节点3新距离 033。更新dist(3)3,prev(3)1。未访问节点中dist最小的是节点3距离3。标记节点3为已访问。检查节点3的邻居节点2和节点4。对于节点2新距离 dist(3) A(3,2) 347。小于当前dist(2)10更新dist(2)7,prev(2)3。这一步是关键我们发现了从1经3到2的路径距离7比直接从1到2距离10更短。对于节点4新距离 3811。更新dist(4)11,prev(4)3。未访问节点中dist最小的是节点2距离7。标记节点2为已访问。检查节点2的邻居节点4。对于节点4新距离 dist(2) A(2,4) 729。小于当前dist(4)11更新dist(4)9,prev(4)2。最后访问节点4。此时所有节点访问完毕。最终结果从1到4的最短距离是9路径通过prev回溯4 - 2 - 3 - 1即 1-3-2-4。这个手动过程清晰地展示了迪杰斯特拉算法的“贪心”特性一旦一个节点被标记为已访问它的最短距离就不再改变。这依赖于一个重要的前提所有边的权重必须非负。如果存在负权边这个前提就不成立因为后面可能通过负权边获得更短路径但该节点已被“关闭”算法就无法得到正确结果。3.2 MATLAB代码实现与逐行解析下面是一个使用邻接矩阵实现的迪杰斯特拉算法MATLAB函数。我加入了大量注释并指出了几个新手极易踩坑的地方。function [dist, prev, path] myDijkstra(A, startNode) % MYDIJKSTRA 使用迪杰斯特拉算法计算单源最短路径 % 输入 % A - n x n 的邻接矩阵A(i,j)表示从i到j的边权无边则为Inf % startNode - 起始节点编号 % 输出 % dist - 1 x n 向量dist(i)为起点到节点i的最短距离 % prev - 1 x n 向量prev(i)为节点i在最短路径上的前驱节点起点为0 % path - 元胞数组path{i}为从起点到节点i的最短路径节点序列可选由prev回溯得到 n size(A, 1); % 节点个数 dist inf(1, n); % 初始化距离为无穷大 visited false(1, n); % 访问标记数组 prev zeros(1, n); % 前驱节点数组 dist(startNode) 0; % 起点到自己的距离为0 for i 1:n-1 % 最多循环n-1次每次确定一个节点的最短路径 % --- 步骤1寻找未访问节点中距离最小的节点 u --- minDist inf; u -1; for v 1:n if ~visited(v) dist(v) minDist minDist dist(v); u v; end end % 如果找不到这样的u说明剩下的节点不可达提前结束 if u -1 break; end visited(u) true; % 标记节点u已访问 % --- 步骤2松弛操作通过u更新其所有邻居的距离 --- for v 1:n % 检查是否存在从u到v的边并且v未被访问 if A(u, v) inf ~visited(v) % A(u,v) inf 判断有边 alt dist(u) A(u, v); % 经由u到v的候选距离 if alt dist(v) % 如果找到更短路径 dist(v) alt; % 更新距离 prev(v) u; % 更新前驱节点 end end end end % --- 可选根据prev数组回溯生成所有路径 --- if nargout 2 % 如果输出参数要求返回path path cell(1, n); for target 1:n if prev(target) 0 target ~ startNode path{target} []; % 不可达 else % 从终点反向回溯到起点 p []; t target; while t ~ 0 p [t, p]; % 将当前节点加入路径头部 t prev(t); end path{target} p; end end end end关键点与避坑指南循环次数外层循环是for i 1:n-1。因为每次循环确定一个点的最短路径起点最初就已确定所以最多还需要确定n-1个点。这是标准写法。寻找最小距离节点上述代码用了简单的线性扫描时间复杂度为O(n)。这在节点很多时比如n10000会成为性能瓶颈。优化方法是使用优先队列最小堆MATLAB中可以用min函数对dist向量进行部分优化但对于严格意义上的O(m log n)复杂度m为边数需要自己实现堆或使用较新的priorityqueueR2023a引入。在数学建模中如果节点数在几千以内线性扫描通常可以接受。松弛条件A(u, v) inf这个判断至关重要。直接使用A(u, v)参与计算如果边不存在值为InfInf参与加法比较会导致逻辑错误或得到Inf。必须先判断边是否存在。负权边检查算法本身没有检查负权边。如果你不确定输入矩阵是否有负权可以在开头添加断言assert(all(A(A~inf) 0), ‘迪杰斯特拉算法要求所有边权非负’);。这是一个很好的编程习惯。3.3 算法复杂度分析与适用场景讨论时间复杂度我们实现的简单版本邻接矩阵线性查找最小距离为 O(n²)。使用邻接表优先队列可以优化到 O((nm) log n)其中m是边数。对于稀疏图m远小于n²优化后的版本快得多。空间复杂度主要是存储邻接矩阵 O(n²) 或邻接表 O(nm)以及几个长度为n的数组。适用场景单源最短路径从一个起点出发到图中所有其他点的最短路径。权重非负这是算法的硬性要求。常见于物理距离、时间、成本等不可能为负的场景。稠密图当边数接近n²时使用邻接矩阵的简单实现可能更直接。MATLAB内置对比shortestpath函数默认的‘positive’方法就是迪杰斯特拉算法的优化实现。在大多数情况下直接调用它是更优选择。一个建模中的常见陷阱将“代价”或“收益”直接作为权重。例如在资源分配问题中边权可能代表收益我们需要求“最大收益路径”。这时不能简单地将收益取负号然后套用迪杰斯特拉算法因为收益为负即代价为正是常态但收益为正代价为负就会违反非负权重要求。此时需要根据具体情况转换问题或使用可以处理负权重的算法。4. 贝尔曼-福特算法能容错负权的“动态规划”如果图中存在负权边迪杰斯特拉算法就失效了。这时我们需要贝尔曼-福特算法。它不仅能够处理负权边还能检测图中是否存在从源点可达的“负权环”即环上各边权重之和为负。如果存在这样的环最短路径问题可能无解因为可以无限次绕环使路径总权值趋于负无穷。4.1 算法原理反复松弛直至收敛贝尔曼-福特算法的思想比迪杰斯特拉更“暴力”也更具包容性。它不再贪心地认为当前最短就是最终最短而是允许多次更新。其核心操作同样是“松弛”Relaxation对于每条边(u, v)检查是否dist(u) w(u,v) dist(v)如果是则更新dist(v)。算法进行n-1轮松弛n为节点数。为什么是n-1轮因为在没有负权环的图中任意两点间的最短路径最多包含n-1条边。经过n-1轮对所有边的全面松弛理论上足够让最短路径信息从源点“传播”到所有节点。第n轮检测进行完n-1轮后再进行一轮额外的松弛操作。如果在这一轮中还有任何dist值能被更新那就说明图中存在从源点可达的负权环。4.2 MATLAB实现与负权环检测以下是贝尔曼-福特算法的MATLAB实现同样使用邻接矩阵输入并包含负权环检测功能。function [dist, prev, hasNegativeCycle] myBellmanFord(A, startNode) % MYBELLMANFORD 贝尔曼-福特算法支持负权边并检测负权环 % 输入 % A - n x n 邻接矩阵 % startNode - 起始节点 % 输出 % dist - 最短距离向量 % prev - 前驱节点向量 % hasNegativeCycle - 布尔值true表示存在从起点可达的负权环 n size(A, 1); dist inf(1, n); prev zeros(1, n); dist(startNode) 0; % 将邻接矩阵转换为边列表方便遍历所有边 [u_list, v_list] find(A ~ inf); % 找到所有非Inf的边 weights zeros(size(u_list)); for k 1:length(u_list) weights(k) A(u_list(k), v_list(k)); end m length(u_list); % 边数 % 主循环进行 n-1 轮松弛 for i 1:n-1 relaxed false; % 标记本轮是否有松弛发生 for k 1:m u u_list(k); v v_list(k); w weights(k); % 松弛操作 if dist(u) ~ inf dist(u) w dist(v) dist(v) dist(u) w; prev(v) u; relaxed true; end end % 如果一轮下来没有任何松弛发生说明已收敛可提前终止 if ~relaxed break; end end % 第 n 轮检测负权环 hasNegativeCycle false; for k 1:m u u_list(k); v v_list(k); w weights(k); if dist(u) ~ inf dist(u) w dist(v) hasNegativeCycle true; fprintf(‘警告检测到从起点可达的负权环最短路径可能无界。\n’); break; end end end代码细节与优化边列表算法需要反复遍历所有边。如果使用邻接矩阵并通过双重循环来遍历在稀疏图上效率很低。因此我们先将邻接矩阵转换为边列表(u, v, w)这样主循环就是 O(m) 而非 O(n²)。提前终止如果某一轮松弛中没有任何距离被更新说明所有最短路径已经找到可以提前结束循环。这对于很多实际图是有效的优化。负权环检测第n轮的检测是必须的。如果检测到环dist和prev数组的输出可能是无意义的因为最短路径不存在。在建模中如果遇到这种情况需要重新审视问题负权环在物理上是否合理是否需要对模型进行修正例如增加约束避免循环4.3 与迪杰斯特拉算法的对比及应用选择为了更清晰地展示两者的区别我将其总结在下表中特性迪杰斯特拉算法贝尔曼-福特算法核心思想贪心算法每次扩展当前已知最短的节点动态规划/暴力松弛反复对所有边进行松弛权重要求必须全部非负可以处理负权边负权环无法处理可能给出错误结果可以检测从源点可达的负权环时间复杂度O(n²) 或 O((nm) log n) (优化后)O(n*m)空间复杂度O(n²) 或 O(nm)O(nm) (存储边列表)适用场景非负权图尤其是稀疏图优化后效率高含负权边的图或需要检测负权环的场景MATLAB内置shortestpath(..., ‘Method’, ‘positive’)shortestpath(..., ‘Method’, ‘mixed’)(对于有向图该选项使用适用于有负权但无环的算法并非严格BF)建模中的选择策略绝大多数情况距离、时间、成本权重均为正直接选用迪杰斯特拉算法或MATLAB内置的‘positive’方法。效率更高。存在负权边例如在金融网络流中某些交易可能产生“收益”可视为负成本必须使用贝尔曼-福特算法或SPFA其队列优化版本。需要检测负循环例如在判断套利机会是否存在货币兑换中是否存在使本金无限增加的循环时贝尔曼-福特算法的检测功能至关重要。小规模图或对效率不敏感即使权重全为正如果图很小n100两种算法都可以。但迪杰斯特拉在概念上更直观。个人经验在数学建模中我建议先明确你的图是否有负权。如果没有放心用迪杰斯特拉。如果有一定要在论文中明确指出并说明使用贝尔曼-福特算法的原因。此外自己动手实现这两个算法即使效率不高对于深刻理解其原理和差异有巨大帮助这比单纯调用黑箱函数更能体现你的建模能力。5. 数学建模实战从静态路径到动态规划掌握了基础算法我们来看看如何在数学建模竞赛中灵活运用。赛题很少会直接问“求最短路径”而是将其嵌入更复杂的场景中。5.1 多目标优化最短路径的变体2018年国赛B题“智能RGV的动态调度策略”虽然主要不是图论问题但其思想可以借鉴。很多时候我们需要优化的不止一个指标。例如在灾后救援物资配送中我们既希望路径时间最短也希望风险最低。这便是一个双目标优化问题。处理方法一加权求和法将时间和风险按照重要程度分配权重合并为一个综合成本。综合成本 α * 时间 β * 风险其中αβ1。 然后以综合成本为边权运行单源最短路径算法。关键在于权重的确定可以用层次分析法AHP或熵权法从题目数据中客观计算这本身就可以成为论文的一个亮点。处理方法二Pareto最优解集法不合并目标而是寻找所有“非支配”路径。一条路径A支配路径B当且仅当A在所有目标上都不比B差且至少在一个目标上严格更好。我们可以修改迪杰斯特拉算法dist数组不再存储一个标量而是存储一个“解集”例如一个结构体数组每个元素包含到达该节点的所有Pareto最优的时间风险向量及其对应路径。松弛操作时将新生成的时间风险向量与当前节点存储的解集进行比较保留所有非支配解。最终终点节点的解集就是所有的Pareto最优路径。决策者可以根据偏好从中选择。第二种方法计算量更大但能提供更全面的决策信息在论文中阐述这种方法能展现你对优化问题的深入理解。5.2 动态权重处理时间依赖的最短路径在交通网络中边的通行时间权重可能是随时间变化的如早晚高峰。这被称为“时变图”或“动态图”上的最短路径问题。经典算法不能直接应用。一种实用的近似建模方法时间离散化时间切片将整个规划期如一天24小时离散化为多个小时间段如每15分钟一个段。构建分层图为每个时间段创建一个原始图的副本节点变为(原始节点, 时间段)。例如节点(A, t)表示在时刻t位于A点。连接边等待边在同一地点可以从(A, t)连接到(A, t1)权重为等待一个时间单位的代价可能为0或很小。旅行边如果从A到B在时刻t出发需要时间travel_time(t)那么就从(A, t)连接到(B, t travel_time(t))权重为travel_time(t)。在新图上运行算法在这个扩展的、静态的分层图上运行标准的迪杰斯特拉算法寻找从(起点, 出发时刻)到任意(终点, T)的最短路径。这种方法将动态问题转化为一个更大规模的静态问题虽然增加了节点数节点数×时间段数但概念清晰易于在MATLAB中实现。关键在于合理选择时间片的粒度在精度和计算复杂度之间取得平衡。5.3 实战案例城市消防站选址问题假设一个赛题要求为一座城市规划若干个消防站目标是使得城市中任意一点发生火灾时消防车都能在最短时间内到达。这可以转化为一个“图中心”或“最小最大距离”问题。我们可以将城市区域抽象为节点如交叉路口道路为边通行时间为权重。首先我们需要求出图中所有点对之间的最短路径距离。对于n个节点这可以通过运行n次迪杰斯特拉算法每次以不同节点为源点来实现时间复杂度为O(n³)或O(n² log n)。对于中等规模城市n~100这是可行的。MATLAB中可以使用distances函数直接计算全源最短路径距离矩阵D其中D(i,j)是i到j的最短时间。设候选消防站位置为节点集合SS可能是所有节点或预先筛选的一部分。对于每一个候选位置s我们计算它到所有其他节点v的距离D(s, v)。其中最远的那个距离称为该站点的“服务半径”或“最坏情况响应时间”R(s) max(D(s, :))。选址目标是找到k个站点使得这k个站点的最大服务半径最小化即最小化max(R(s1), R(s2), …, R(sk))或者使得所有节点到其最近站点的平均距离最小化。这是一个组合优化问题当k固定且较小时可以枚举所有组合当k较大或节点多时可能需要使用启发式算法如贪心算法每次选择能使最大覆盖半径减少最多的点或模拟退火、遗传算法等。在论文中你需要清晰地展示图的构建过程、全源最短路径距离矩阵的计算、选址模型的建立目标函数和约束、求解算法的选择与实现步骤。将图论算法作为整个模型的一个核心模块嵌入其中。6. MATLAB高效实现与调试技巧理论懂了代码写了但真正跑起来可能还会遇到各种问题。下面分享一些我在使用MATLAB解决图论问题时积累的实战技巧。6.1 利用内置函数与工具箱除非赛题明确要求或你需要实现特殊变种否则优先使用MATLAB内置的高效函数。创建图G graph(s, t, w)或G digraph(s, t, w)。最短路径[path, dist] shortestpath(G, s, t); % 默认使用‘auto’对正权图用迪杰斯特拉 [path, dist] shortestpath(G, s, t, ‘Method’, ‘positive’); % 强制使用迪杰斯特拉 [path, dist] shortestpath(G, s, t, ‘Method’, ‘mixed’); % 允许负权但要求无环使用Johnson算法等全源最短路径D distances(G);返回一个距离矩阵。这是计算多源点最短路径最方便的方法。最近邻搜索[nodeID, dist] nearest(G, s, d);查找从节点s出发、距离在d以内的所有节点。可视化plot(G)快速绘图。对于大规模图可以使用plot(G, ‘Layout’, ‘force’)或‘subspace’等布局让图更清晰。6.2 处理大规模数据的性能优化当节点数上万时需要注意性能。使用稀疏矩阵如果使用邻接矩阵务必用sparse创建稀疏矩阵。A sparse(n, n); A(1,2) 10; A(1,3) 3; % ... 赋值方式一样 % 或者从边列表创建 A sparse(s, t, w, n, n);稀疏矩阵只存储非零元素能极大节省内存和计算时间。shortestpath函数天然支持稀疏矩阵。避免在循环中动态增长数组这是MATLAB性能杀手。在实现算法时预先分配好dist,visited,prev等数组。dist inf(1, n); % 正确一次性分配 % 错误在循环中 dist [dist, ...]向量化操作在可能的情况下用矩阵运算代替循环。例如在迪杰斯特拉算法的松弛步骤中完全向量化比较困难但寻找最小距离节点可以用[minDist, u] min(dist .* ~visited);来实现其中.* ~visited将已访问节点的距离置为Inf如果dist中对应位置是Inf需要额外处理。不过对于教学和清晰度使用循环通常更易懂。6.3 常见错误与调试方法“索引超出矩阵维度”最常见错误。检查节点编号是否从1开始MATLAB索引你的s,t向量中是否有大于节点总数n的编号。使用max([s(:); t(:)])来确认最大节点号。路径不存在输出为NaN或空检查图是否连通对于无向图或终点是否从起点可达对于有向图。可以使用conncomp(G)查看连通分量或distances(G, startNode)查看结果如果距离是Inf则不可达。结果与预期不符首先检查图的构建是否正确。将小规模图用plot(G, ‘EdgeLabel’, G.Edges.Weight)画出来肉眼核对边和权重。手动计算对于3-5个节点的小例子手动用算法跑一遍与程序输出对比。输出中间变量在你自己实现的算法中在每一轮循环后打印出dist和visited数组看更新过程是否符合逻辑。权重正负再次确认是否有负权边并选择了正确的算法。性能极慢对于大规模图如果使用自己写的O(n²)迪杰斯特拉慢是正常的。换用内置函数shortestpath或distances它们经过了高度优化。如果必须自己实现确保使用了邻接表或稀疏矩阵和优先队列数据结构。最后一个最重要的建议为你的图论代码编写简单的测试用例。包括一个已知答案的小型网络以及一个包含负权边如果你用BF算法的测试。在每次修改代码后运行这些测试确保核心逻辑正确。这能节省你大量的调试时间。
返回列表