迪杰斯特拉算法(Dijkstra’s Algorithm):带权图中的最短路径求解
迪杰斯特拉算法是一种用于带权有向图(边权重为非负数) 的最短路径算法,核心是从起点出发,逐步找到到所有其他节点的总权重最小的路径。与广度优先算法(仅适用于边权重相同的场景,如 “最少换乘”)不同,它能处理边权重不同的情况(如 “最短时间”“最少成本”),广泛应用于导航系统、网络路由等领域。
迪杰斯特拉算法的核心思想
基本原理
算法基于贪心策略:每次选择 “当前已知最短路径可达的节点”,通过该节点更新其邻居的最短路径,直到所有节点都被处理。
- 核心目标:找到从起点到其他所有节点的最短路径(总权重最小)。
- 适用条件:图中边的权重必须为非负数(若存在负权重,算法会失效,需用贝尔曼 - 福特算法)。
关键概念
- 起点:路径的起始节点(如 “出发地 A”)。
- 权重:边的 “代价”(如时间、距离、成本等,必须≥0)。
- 最短路径:从起点到某节点的所有路径中,总权重最小的路径。
- 已处理节点:已确定最短路径的节点(一旦处理,不再更新其路径)。
迪杰斯特拉算法的步骤
以 “从 A 到 G 的最短路径” 为例(图中边权重为时间,单位:分钟),算法步骤如下:
步骤 1:初始化数据
- 图的表示:用邻接表存储节点间的权重(如
A的邻居为B(6)、C(3),表示 A 到 B 需 6 分钟,A 到 C 需 3 分钟)。 - 距离表(costMap):记录从起点到各节点的当前最短距离,初始时起点距离为 0,其他节点为 “无穷大”(表示暂不可达)。
- 父节点表(parentMap):记录各节点的前序节点,用于最终回溯路径。
- 已处理集合(processed):记录已确定最短路径的节点(初始时仅包含起点)。
步骤 2:迭代寻找最短路径
- 选择当前最短路径可达的节点:从 “未处理节点” 中,选出距离起点最近的节点(记为
u)。 - 更新
u的邻居的最短路径:对于u的每个邻居v,计算 “起点→u→v” 的总距离(起点到 u 的距离 + u 到 v 的权重)。若该距离小于v当前的已知最短距离,则更新v的距离和父节点(设为u)。 - 标记
u为已处理:一旦u被处理,其最短路径不再变更(因权重非负,后续路径不可能更短)。 - 重复:直到所有节点都被处理,或找到目标节点的最短路径。
示例:从 A 到 G 的最短路径计算
图结构(边权重为时间)
1 | A → B(6);A → C(3) |
算法执行过程
- 初始化:
- 距离表:
A:0,B:∞,C:∞,D:∞,E:∞,F:∞,G:∞ - 已处理:
{A}
- 距离表:
- 第一次迭代:
- 未处理节点中,A 的邻居
B(6)和C(3),选最近的C(距离 3)。 - 更新C的邻居:
B:A→C→B总距离3+2=5(小于原∞)→ 更新B:5,父节点C。D:A→C→D总距离3+5=8→ 更新D:8,父节点C。
- 已处理:
{A, C}
- 未处理节点中,A 的邻居
- 第二次迭代:
- 未处理节点中,最近的是
B(距离 5)。 - 更新B的邻居D:
A→C→B→D总距离5+1=6(小于原 8)→ 更新D:6,父节点B。
- 已处理:
{A, C, B}
- 未处理节点中,最近的是
- 第三次迭代:
- 未处理节点中,最近的是
D(距离 6)。 - 更新D的邻居:
E:6+3=9→ 更新E:9,父节点D。F:6+2=8→ 更新F:8,父节点D。
- 已处理:
{A, C, B, D}
- 未处理节点中,最近的是
- 第四次迭代:
- 未处理节点中,最近的是
F(距离 8)。 - 更新
F的邻居G:8+3=11→ 更新G:11,父节点F。 - 已处理:
{A, C, B, D, F}
- 未处理节点中,最近的是
- 第五次迭代:
- 未处理节点中,最近的是
E(距离 9)。 - 更新
E的邻居G:9+1=10(小于原 11)→ 更新G:10,父节点E。 - 已处理:
{A, C, B, D, F, E}
- 未处理节点中,最近的是
- 结束:所有节点处理完毕,
G的最短距离为 10,路径为A→C→B→D→E→G。
代码实现与解析
核心数据结构
graphMap:邻接表,存储节点及其邻居的权重(Map<String, Map<String, Integer>>)。costMap:记录起点到各节点的当前最短距离(Map<String, Integer>)。parentMap:记录路径的父节点,用于回溯完整路径(Map<String, String>)。processed:已处理节点集合(Set<String>)。
代码逻辑
1 | import java.util.*; |
算法优化与时间复杂度
时间复杂度
- 基础实现:每次查找下一个最短节点需遍历所有未处理节点(
O(V)),共需O(V)次迭代,总时间复杂度为O(V²)(V为节点数)。 - 优化实现:用优先队列(最小堆) 存储未处理节点,查找最短节点的时间从
O(V)降至O(logV),总时间复杂度优化为O((V + E)logV)(E为边数),适合边数较少的稀疏图。
局限性
- 仅适用于非负权重的图(若存在负权重,已处理节点的最短路径可能被后续更短路径推翻)。
- 无法处理有环图中的负权重环(会导致路径无限缩短)。
应用场景
- 导航系统:计算两地之间的最短时间 / 距离路径(如高德地图、Google Maps)。
- 网络路由:路由器之间计算数据传输的最短路径(如 OSPF 协议)。
- 资源分配:在带成本的任务依赖图中,找到从起点到目标的最低成本路径。
总结
迪杰斯特拉算法通过贪心策略,逐步确定从起点到各节点的最短路径,核心是 “每次处理当前最短可达节点并更新其邻居”。它能高效处理非负权重的带权图,是解决最短路径问题的经典算法
迪杰斯特拉算法
之前在进行最短路径计算时使用了广度优先算法,但是找出的只是换乘最少的路径,每次所用的时间其实是不一样的,如果要找出用时最短的路径,可以使用迪杰斯特拉算法(Dijkstra)
如上述路线,从A到D,如果使用广度优先算法的话会得到A->B->D,花费7分钟。而其实最短时间是A->C->B->D,花费6分钟。
迪杰斯特拉算法包含四个步骤
找出可在最短时间内到达的节点,站在A点,不知道该前往B点还是C点时,此时由于还不知道前往终点需要多长时间,假设是无穷大。此时C点是最近的
第一步算出来C点是距离起点最近的,这一步需要计算出经过C点到其他各邻居节点所需的时间
A->C->B 需要5分钟
A->C->D需要8分钟
OK。此时找到了一条前往B的更短路径。直接前往B需要6分钟,经C之后前往B只需要5分钟。
所以更新一下到各节点的时间开销
- 前往B点的路径时间从6分钟缩短到了5分钟
- 前往终点的时间从无穷大缩短到了8分钟
重复前两步操作。重复第一步时排除掉第一次选出的C点,所以选出的点是B点。重复第二步更新B点所有邻居节点的开销。
此时发现达到终点的时间变为了6分钟。
最终算出到达终点的最短时间
使用的是贪心算法
在迪杰斯特拉算法中,给每段都分配了一个权重,找到的是总权重最小的路径
举个栗子
计算A到G的总权重最小路径
1 | public class Dijkstra { |


v1.3.10