算法宗门---迪杰斯特拉Dijkstra(最短路径算法)

一.迪杰斯特拉算法的由来(背后的故事)

上面这位"帅哥"就是荷兰计算机科学家 艾兹赫尔・W・迪杰斯特拉(Edsger W. Dijkstra),他在 1956 年发明迪杰斯特拉算法。

背后还有一段有趣的小插曲:当时迪杰斯特拉和妻子在阿姆斯特丹逛街,妻子问他 "从当前位置到市中心的所有路线中,哪条最短?",这个日常问题激发了他的思考。他当时正为 "如何高效找到图中两点间最短路径" 的问题发愁,这次闲聊让他茅塞顿开,回家后仅用 20 分钟就构思出了这个算法的核心逻辑。

迪杰斯特拉本人是结构化编程的先驱,他的算法核心思想是 "贪心策略"------ 每次选择当前距离起点最近未访问节点,逐步扩展到所有节点,最终得到起点到所有节点的最短路径。这个算法最初是为了解决 "从鹿特丹到格罗宁根的最短路线" 这类实际交通问题,后来成为图论中最经典的最短路径算法之一。

二、迪杰斯特拉算法的核心作用

简单来说,迪杰斯特拉算法的核心作用是:在一个带权(权重为非负数)的有向 / 无向图中,找到从一个指定起点到其他所有节点的最短路径。它的典型应用场景包括:

  1. 导航软件(如高德、百度地图)计算两点间最短 / 最快路线;
  2. 网络路由协议(如 OSPF)选择最优数据传输路径;
  3. 物流配送规划(如外卖、快递的最优配送路线);
  4. 游戏中角色移动的最短路径计算。

三、迪杰斯特拉算法的具体可执行代码

下面提供基于 邻接表 的 Python 实现(邻接表是图的高效存储方式,适合稀疏图),代码包含详细注释,可直接复制运行。

代码实现(优先队列优化版,时间复杂度 O (E log V))
python 复制代码
import heapq

def dijkstra(graph, start):
    """
    迪杰斯特拉算法实现(优先队列优化)
    :param graph: 邻接表表示的图,格式为 {节点: [(邻居节点, 边权重), ...]}
    :param start: 起点节点
    :return: 起点到各节点的最短距离字典,前驱节点字典(用于回溯路径)
    """
    # 1. 初始化:所有节点的最短距离设为无穷大,起点设为0
    INF = float('inf')
    shortest_dist = {node: INF for node in graph}
    shortest_dist[start] = 0
    
    # 前驱节点字典,用于回溯最短路径
    predecessor = {node: None for node in graph}
    
    # 2. 优先队列(小顶堆),存储 (当前距离, 节点),初始放入起点
    # heapq默认是小顶堆,保证每次取出距离最小的节点
    priority_queue = []
    heapq.heappush(priority_queue, (0, start))
    
    # 3. 记录已访问的节点(避免重复处理)
    visited = set()
    
    # 4. 核心循环:处理优先队列中的节点
    while priority_queue:
        # 取出当前距离最小的节点
        current_dist, current_node = heapq.heappop(priority_queue)
        
        # 如果该节点已访问,跳过(避免重复处理)
        if current_node in visited:
            continue
        # 标记为已访问
        visited.add(current_node)
        
        # 遍历当前节点的所有邻居
        for neighbor, weight in graph[current_node]:
            # 跳过已访问的邻居(优化)
            if neighbor in visited:
                continue
            
            # 计算从起点到邻居的临时距离
            temp_dist = current_dist + weight
            
            # 如果临时距离比已知的最短距离更小,更新
            if temp_dist < shortest_dist[neighbor]:
                shortest_dist[neighbor] = temp_dist
                predecessor[neighbor] = current_node  # 更新前驱节点
                # 将更新后的距离和邻居节点加入优先队列
                heapq.heappush(priority_queue, (temp_dist, neighbor))
    
    return shortest_dist, predecessor

# 辅助函数:根据前驱节点字典,回溯从起点到目标节点的最短路径
def get_shortest_path(predecessor, start, target):
    """
    回溯最短路径
    :param predecessor: 前驱节点字典
    :param start: 起点
    :param target: 目标节点
    :return: 最短路径列表(如 [A, B, C])
    """
    path = []
    current = target
    # 从目标节点往回找,直到起点
    while current is not None:
        path.append(current)
        current = predecessor[current]
        # 防止循环(如果目标节点不可达)
        if len(path) > len(predecessor):
            return []
    # 反转路径,得到从起点到目标的顺序
    path.reverse()
    # 如果路径的第一个节点不是起点,说明不可达
    return path if path[0] == start else []

# ------------------- 测试代码 -------------------
if __name__ == "__main__":
    # 构建示例图(邻接表)
    # 节点:A, B, C, D, E
    # 边:A->B(2), A->C(5), B->C(1), B->D(3), C->D(2), C->E(4), D->E(1)
    graph = {
        'A': [('B', 2), ('C', 5)],
        'B': [('C', 1), ('D', 3)],
        'C': [('D', 2), ('E', 4)],
        'D': [('E', 1)],
        'E': []
    }
    
    # 执行迪杰斯特拉算法,起点为A
    start_node = 'A'
    shortest_dist, predecessor = dijkstra(graph, start_node)
    
    # 输出起点到各节点的最短距离
    print(f"起点 {start_node} 到各节点的最短距离:")
    for node, dist in shortest_dist.items():
        print(f"{start_node} -> {node}: {dist}")
    
    # 输出从A到E的最短路径
    target_node = 'E'
    path = get_shortest_path(predecessor, start_node, target_node)
    print(f"\n从 {start_node} 到 {target_node} 的最短路径:{' -> '.join(path)}")
代码运行结果
复制代码
起点 A 到各节点的最短距离:
A -> A: 0
A -> B: 2
A -> C: 3
A -> D: 5
A -> E: 6

从 A 到 E 的最短路径:A -> B -> C -> D -> E

迪杰斯特拉算法,重要的点在于距现在的位置最短、未访问。A分别到B、C、D、E的距离逐渐增大,所以先找最小的。

代码关键部分解释
  1. 邻接表graph 字典是图的核心存储方式,键是节点,值是该节点的邻居和边权重的列表,适合稀疏图(大部分节点无连接)。
  2. 优先队列(小顶堆)heapq 模块实现,保证每次取出当前距离起点最近的节点,这是迪杰斯特拉 "贪心" 思想的核心。
  3. 已访问集合:避免重复处理同一节点,减少无效计算。
  4. 前驱节点字典:用于回溯最短路径,比如从 E 往回找 D→C→B→A,反转后得到完整路径。

四、总结

  1. 由来:由荷兰科学家迪杰斯特拉受日常问路启发发明,核心是 "贪心策略",解决带权无负边图的最短路径问题。
  2. 作用:找到图中起点到所有节点的最短路径,广泛应用于导航、路由、物流等场景(仅支持非负权边)。
  3. 代码核心:用优先队列(小顶堆)优化,每次选距离最小的节点扩展,更新邻居的最短距离,最终得到所有节点的最短路径。
相关推荐
练习时长一年9 小时前
LeetCode热题100(爬楼梯)
算法·leetcode·职场和发展
朔北之忘 Clancy9 小时前
2020 年 6 月青少年软编等考 C 语言一级真题解析
c语言·开发语言·c++·学习·算法·青少年编程·题解
_codemonster9 小时前
计算机视觉入门到实战系列(九) SIFT算法(尺度空间、极值点判断)
深度学习·算法·计算机视觉
sinat_2869451910 小时前
AI Coding LSP
人工智能·算法·prompt·transformer
星马梦缘10 小时前
算法与数据结构
数据结构·c++·算法·动态规划·克鲁斯卡尔·kahn
2501_9434691510 小时前
【无标题】
数据结构·算法
_codemonster10 小时前
计算机视觉入门到实战系列(八)Harris角点检测算法
python·算法·计算机视觉
Snow_day.10 小时前
有关排列排列组合(1)
数据结构·算法·贪心算法·动态规划·图论
dora11 小时前
【开发火星地平线辅助】智商不够,编程来凑
算法