抛出问题
在广度优先搜索算法里面我们在从双子峰到金门大桥的图里面寻找的是换乘最短的路径,但是我们需要找出最快的路径应该怎么选择?
如下图:
因此我们可以使用另一种算法——狄克斯特拉算法(Dijkstra’s algorithm)。
算法简介
狄克斯特拉算法包含4个步骤:
1、找出“最便宜”的节点,即可在最短时间内到大的节点。
2、更新该节点的邻居的开销,即找出与这个节点有的映射关系的所有节点。
3、重复这个过程,直到对图中的每个节点都这样做了。
4、计算最终路径。
狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。
带权重的图被称为加权图(weighted graph),不带权重的图被称为非加权图(unweighted graph)。
要计算非加权图中的最短路径可以使用广度优先搜索,而要计算加权图中的最短路径可使用狄克斯特拉算法。
在有向图中,每条边都是单向的;而在无向图中,每条边都是一个环。
狄克斯特拉算法只适用于有向无环图(directed acyclicgraph, DAG)。
当然,如果图中有负权边,狄克斯特拉算法也不适用。
代码实现
我们以下图为例使用代码来实现一下狄克斯特拉算法:
首先,我们用三个散列表来分别表示各节点之间的关系和边的权重:
随着算法的进行,我们将不断的更新散列表costs和parents。
graph = {}
graph['start']['a'] = 6
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['fin'] = 1
graph['b'] = {}
graph['b']['a'] = 3
graph['b']['fin'] = 5
graph['fin'] = {} # 终点没有任何邻居
# 接下来,用一个散列表来存储每个节点的开销
infinity = float('inf') # python中无穷大的表示方法
costs = {}
costs['a'] = 6
costs['b'] = 2
costs['fin'] = infinity
# 然后用一个散列表存储父节点
parents = {}
parents['a'] = "start"
parents['b'] = "start"
parents['fin'] = None
# 最后,我们需要一个数组用于记录处理过的节点
processed = []
# 我们先来实现一下找出开销最低的节点的方法
def find_lowest_cost_node(costs):
lowest_cost = float('inf')
lowest_cost_node = None
for node in costs:
cost = costs[node]
if cost < lowest_cost and node not in processed:
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
# 接着我们来实现一下算法
# 在未处理的节点中找出开销最小的节点
node = find_lowest_cost_node(costs)
# 处理所有节点
while node is not None:
cost = costs[node]
neighbors = graph[node] # 找出这个节点的所有邻居
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if costs[n] > new_cost: # 如果经当前节点前往该邻居更近
cost[n] = new_cost # 更新该邻居的开销
parents[n] = node # 将该邻居的父节点设置为当前节点
processed.append(node) # 将当前节点标记为已处理过
node = find_lowest_cost_node(costs) # 找出接下来要处理的节点