📜  离散数学Dijkstra的算法(1)

📅  最后修改于: 2023-12-03 15:11:25.520000             🧑  作者: Mango

离散数学之Dijkstra's算法

Dijkstra's算法是一种用于计算带有非负边权的加权图中单源最短路径的贪心算法。该算法源于E.W. Dijkstra的1956年论文,虽然当时只适用于有些限制,但现在最常用于路径寻找和网络路由。

该算法属于贪心算法的一种,它的基本思想是:对于起点S到图中其他节点的所有路径,首先到达权值最小的节点V,然后再从V到达与其相邻的权值最小的节点;依此类推,直到到达终点T。

算法描述
  1. 把图中所有顶点分成两个集合:已知最短路径的顶点集合和未知最短路径的顶点集合。开始时,已知最短路径的顶点集合为空,未知最短路径的顶点集合包含图中所有顶点。
  2. 确定起点S到每个顶点的距离(初始状态下,S本身的距离为0,其它节点为无穷大)。
  3. 从未知最短路径顶点集合中选取一个顶点V,使得S到V的距离最小。
  4. 现在已知从S到V的最短路径。对于所有与V相邻的未知最短路径顶点,更新它们到起点的距离。特别地,如果从起点S到一个顶点V的路径通过顶点W可以缩短原来的路径,那么更新这个顶点的距离。
  5. 重复步骤3和4,直到确定了从S到所有的顶点的最短路径。
算法实现

我们使用Python实现Dijkstra's算法。

from heapq import heappush, heappop

def dijkstras_algorithm(adjacency_list, start_node):
    # 初始化距离字典
    distances = {node: float('inf') for node in adjacency_list}
    distances[start_node] = 0

    pq = [(0, start_node)]
    while len(pq) > 0:
        current_distance, current_node = heappop(pq)

        # 如果当前顶点已经被访问过,则跳过
        if current_distance > distances[current_node]:
            continue

        # 遍历当前顶点的邻接点
        for neighbor, weight in adjacency_list[current_node].items():
            distance = current_distance + weight
            # 更新距离
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heappush(pq, (distance, neighbor))
    
    return distances
算法分析

Dijkstra's算法时间复杂度为O(E log V),其中E是边数,V是顶点数。使用堆来实现最小优先队列,可优化算法性能,使其接近O(E + V log V)的时间复杂度。

算法还有一些局限性,例如,它只适用于非负边权图,如果图中有负边,则需要使用Bellman-Ford算法。此外,它还要求直接连接两个节点的边权值必须大于0,如果边权可以为0,则需要对算法进行一些修改。

参考文献
  1. Dijkstra, E. W. (1959). "A note on two problems in connexion with graphs." Numerische Mathematik 1: 269–271.
  2. Cormen, T. H.; Leiserson, C. E.; Rivest, R. L.; Stein, C. (2009). Introduction to Algorithms (3rd ed.). MIT Press and McGraw-Hill. Print.