一. DJKSTRA算法概述
我们可以将地图抽象为Graph的数据结构,然后利用Graph的广度优先遍历算法(Breadth-First Search, BFS)解决无权重的High-Level的地图级别的规划。但是实际应用场景中,地图中各个路径所代表的Graph的边的权重都是不同的,比如距离长的Edge权重就应该比较低;交通拥堵的Edge权重就应该低等等。对于有权重的Graph如何进行最短路径规划呢,Dijkstra算法可以解决这个问题。
Dijkstra算法是一种有权图(Graph)的单源最短路径求解算法,给定一个起点,使用Dijkstra算法可以得到起点到其它所有节点的最短路径。Dijkstra算法要求图(Graph)中所有边的权重都为非负值,只有保证了这个条件才能该算法的适用性和正确性。
算法思想:偷个懒,摘自百度百科
按路径长度递增次序产生算法:
把顶点集合V分成两组:
(1)S:已求出的顶点的集合(初始时只含有源点V0)
(2)V-S=T:尚未确定的顶点集合
将T中顶点按递增的次序加入到S中,保证:
(1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度
(2)每个顶点对应一个距离值
S中顶点:从V0到此顶点的长度
T中顶点:从V0到此顶点的只包括S中顶点作中间顶点的最短路径长度
依据:可以证明V0到T中顶点Vk的,或是从V0到Vk的直接路径的权值;或是从V0经S中顶点到Vk的路径权值之和
(反证法可证)
求最短路径步骤
算法步骤如下:
G={V,E}
1. 初始时令 S={V0},T=V-S={其余顶点},T中顶点对应的距离值
若存在<V0,Vi>,d(V0,Vi)为<V0,Vi>弧上的权值
若不存在<V0,Vi>,d(V0,Vi)为∞
2. 从T中选取一个与S中顶点有关联边且权值最小的顶点W,加入到S中
3. 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0到Vi的距离值缩短,则修改此距离值
重复上述步骤2、3,直到S 中包含所有顶点,即W=Vi为止
二. 示例问题
给出如下带权重的图,求从A到G的最短路径
第一步:
构建一个记录最短路径和距离并用来计算权重的表格。初始化这个表格,除了起点A,已知距离为0,其它距离初始化为无穷大。
A | B | C | D | E | F | G |
0 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
第二步:
以A为起点,可以得到从A出发的几条边,更新最短距离,并标记A为已计算过最小路径
找到:
A - B; 权重8
A - D; 权重10
A - E; 权重12
第三步:
遍历上一步找出的几条路径,选择最短路径,并计算其临边的所有路径,更新表格,并标记B为已找过的顶点
找到:
A - B - C 14
A - B - F 20
第四步:不断重复第三步,直到所有顶点都找完为止
第五步:得到结果 A - B - F - G
三. Python代码实现
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3 # @author: Asp1rant
4
5
6 def djkstra(graph, start, end):
7 path_set = set() # 已求的路径集合
8 priority_dic = {}
9 for k in graph.keys():
10 priority_dic[k] = [9999, False, ""] # 权重表构建为一个3维数组,分别是:最小路径代价,是否计算过最小边,最小路径
11 priority_dic[start][0] = 0
12
13 # 判断权重表中所有路点是否添加完毕
14 def isSelectAll():
15 ret = True
16 for val in priority_dic.values():
17 if not val[1]:
18 ret = False
19 break
20 return ret
21
22 while not isSelectAll():
23 find_point = start
24 find_path = start
25 min_distance = 9999
26 for path in path_set:
27 end_point = path[-1]
28 path_distance = priority_dic[end_point][0]
29 if path_distance < min_distance and not priority_dic[end_point][1]:
30 find_point = end_point
31 find_path = path
32 min_distance = path_distance
33 find_distance = priority_dic[find_point][0]
34 neighbors = graph[find_point]
35 for k in neighbors.keys():
36 p = find_path + "-" + k
37 weight = find_distance + neighbors[k]
38 path_set.add(p)
39 if weight < priority_dic[k][0]:
40 priority_dic[k][0] = weight
41 priority_dic[k][2] = p
42 priority_dic[find_point][1] = True
43
44 return priority_dic[end]
45
46
47 if __name__ == '__main__':
48 # 用于测试的图
49 graph = {
50 "A": {"B": 8, "D": 10, "E": 12},
51 "B": {"C": 6, "F": 12},
52 "C": {"F": 8},
53 "D": {"E": 10, "G": 30},
54 "E": {"F": 10},
55 "F": {"G": 12},
56 "G": {}
57 }
58 result = djkstra(graph, "A", "G")
59 print(result)
输出结果:
[32, True, 'A-B-F-G']