图里面有一种经典问题,叫做最短路径问题。现实场景也非常的多,比如,你想从A地到D地,但有很多条路径可以到达,中间要经过多次的中转,每一段路花费的时间都不一样,那么找到一条路径,让从A到D花费的时间最短,就是一个最短路径问题。
抽象一下就是一个有权图中,找到连通两个点权重最小的那条路径。如下图,如果想要知道A点到D点权值最短的路径,就是个最短路径问题。
迪杰斯特拉(Dijkstra)算法就是解决这个问题的经典算法。
他的思路就是通过遍历所有的节点,不停的更新初始节点到每一个节点的最短距离。
这么说有点抽象,那就举个例子。首先选定一个初始点,假设是A,然后开始遍历。A到A的距离是0,开始遍历下一个节点。B节点,那A-B直接最短距离就是10,再遍历和A相邻的另一个节点F,A-F最短距离为11。这样相当于就遍历了A相邻的第一层节点。
然后遍历距离A最近的那个节点周围的节点。从目前来看A-B=10,A-F=11,所以从B开始。B的相邻节点C,A-B-C的距离为(A-B)+(B-C)= 28,同理可以算出来A-B-I = 22,A-B-G = 26。
然后再从距离A最近的节点开始计算,这时候比较的就是F(11),C(28),I(22),G(26)几个节点。发现F最近,于是再从F开始计算。F周围的节点发现又见到了G了,目前A到I的最短路径是A-B-G=26。我们就需要比较一下新的路径有没有更近一些,需不需要更新最短路径。然后发现A-F-G=11+17=28>26,并没有更近。所以A到G的最短路径仍然保持不变是A-B-G。
用相同的方式,每次都遍历距离A最近的那个节点,而距离的远近是根据更新到目前为止到达该节点的最短路径。
熟悉图遍历的话,发现这种遍历方法不是深度优先,也不算是广度优先,可以称之为“距离”优先的遍历。而如果把这里面所有节点之间的距离设置成为1,就变成了一个无权图,Dijkstra就退化成为了广度优先遍历算法。
有了这个认知,其实稍微改造一下广度优先遍历的算法,就可以得到Dijkstra最短路径算法啦。
需要改变的就是要把广度优先遍历中使用的队列,变成一个“优先队列”。原来的队列是先进先出,而“优先队列”是根据权重,也就是Dijkstra算法中的距离,排序,权重小的先出。
具体的代码实现如下
def sp_dijkstra(graph,start_vertex_index): # 初始化前置结点列表 p = list(None for i in range(len(graph.vertex_list))) # 初始化最短距离列表 d = list(None for i in range(len(graph.vertex_list))) # 初始化优先队列 q = queue.PriorityQueue() # 初始化起始结点,起始结点到本身的距离为0 d[start_vertex_index] = 0 # 初始化起始结点的前置结点,为本身 p[start_vertex_index] = start_vertex_index # 将初始结点和与初始结点的距离放入优先队列,初始节点距离为0 q.put((0,(graph.vertex_list[start_vertex_index],start_vertex_index))) # 记录节点是否已经被计算最短路径 v_short = [] # 开始处理 while not q.empty(): v,i = q.get()[1] if v_short.__contains__(i): continue else: v_short.append(i) # 遍历v的相邻结点 e = v.firstedge while e is not None: # 初始节点通过i节点到达e节点距离 temp_d = d[i] + e.weight # 更新最短距离 if (d[e.adjvex] is None) or (d[e.adjvex] > temp_d) : d[e.adjvex] = temp_d p[e.adjvex] = i q.put((d[e.adjvex],(graph.vertex_list[e.adjvex],e.adjvex))) e = e.nxt print('最短距离:',d) print('前置结点:',p)
尝试求一下上面那张图,从A点出发的最短路径。
graph_matrix = np.array([[0, 10, 0, 0, 0, 11, 0, 0, 0], [10, 0, 18, 0, 0, 0, 16, 0, 12], [0, 18, 0, 22, 0, 0, 0, 0, 8], [0, 0, 22, 0, 20, 0, 24, 16, 21], [0, 0, 0, 20, 0, 26, 0, 7, 0], [11, 0, 0, 0, 26, 0, 17, 0, 0], [0, 16, 0, 24, 0, 17, 0, 19, 0], [0, 0, 0, 16, 7, 0, 19, 0, 0], [0, 12, 8, 21, 0, 0, 0, 0, 0]])vex_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']g = graph.Adgraph()g.build(vex_list, graph_matrix)sp_dijkstra(g,0)
结果如下
节点: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']最短距离:[0, 10, 28, 43, 37, 11, 26, 44, 22]前置结点:[0, 0, 1, 8, 5, 0, 1, 4, 1]
END
作者:锅哥不姓郭
图片:网络(侵删)