Dijkstra算法

5 阅读2分钟

Dijkstra算法是一种用于在带非负权重的图中找到单源最短路径的经典算法。以下是该算法的关键点总结:

算法步骤

  1. 初始化
  • 将起始节点的距离设为0,其他所有节点的距离设为无穷大(∞)。

  • 使用优先队列(通常为最小堆)存储节点及其当前最短距离,起始节点首先入队。

  • 维护一个集合记录已处理的节点。

  1. 处理节点
  • 从优先队列中取出距离最小的节点u

  • u已处理过,则跳过;否则标记为已处理。

  • 遍历u的所有邻居v

    • 计算从起始节点经uv的路径长度:alt = dist[u] + weight(u, v)

    • alt < dist[v],则更新v的距离,并将v重新加入优先队列。

  1. 终止条件
  • 当优先队列为空时,算法结束。此时dist[]中存储了所有节点的最短距离。

关键特性

  • 贪心策略:每次选择当前最短路径的节点,确保非负权重下局部最优即全局最优。

  • 时间复杂度

    • 使用二叉堆:O((V + E) log V),适用于稀疏图。

    • 使用斐波那契堆:O(E + V log V),理论最优但实现复杂。

    • 邻接矩阵遍历:O(V²),适合稠密图。

  • 适用条件:边权重必须非负,否则可能无法得到正确结果。

示例与验证

图结构

  • 节点:A, B, C, D

  • 边:A→B(3), A→C(5), B→C(1), B→D(2), C→D(3)

执行过程

  1. 初始队列:[(0, A)],距离:A=0,其他∞。

  2. 处理A,更新B(3)、C(5),队列变为[(3, B), (5, C)]

  3. 处理B,更新C(4)、D(5),队列变为[(4, C), (5, D)]

  4. 处理C,尝试更新D(7 > 5,不更新),队列剩余[(5, D)]

  5. 处理D,结束。

结果:最短距离为A→B(3), A→B→C(4), A→B→D(5)

代码实现(Python示例)


import heapq

def dijkstra(graph, start):

    distances = {node: float('infinity') for node in graph}

    distances[start] = 0

    heap = [(0, start)]

    processed = set()

    while heap:

        current_dist, u = heapq.heappop(heap)

        if u in processed:

            continue

        processed.add(u)

        for v, weight in graph[u].items():

            distance = current_dist + weight

            if distance < distances[v]:

                distances[v] = distance

                heapq.heappush(heap, (distance, v))

    return distances

# 示例图

graph = {

    'A': {'B': 3, 'C': 5},

    'B': {'C': 1, 'D': 2},

    'C': {'D': 3},

    'D': {}

}

print(dijkstra(graph, 'A'))  # 输出:{'A':0, 'B':3, 'C':4, 'D':5}

注意事项

  • 负权边:Dijkstra算法无法处理,需改用Bellman-Ford算法。

  • 路径回溯:可通过记录前驱节点(prev[]数组)重建最短路径。

  • 重复入队:允许队列中存在同一节点的多个实例,但已处理节点会被跳过。

通过以上步骤和示例,Dijkstra算法能够高效求解非负权重图的单源最短路径问题。