在(五)里面我们简单说了广度优先搜索算法,它可以找出到达目标节点的段数最少的路径;但是像下图这样(书上图):
从双子峰到金门大桥用时最少的路径,怎么求呢?如果 用广度优先搜索,最后得到的最短路径是3段,即最上面的那条路径,但不是用时最短的,这节我们介绍一种新的算法:狄克斯特拉算法。
狄克斯特拉算法包含四个步骤:
1.先找出最便宜的节点,即在最短时间可以到达的节点
2.更新该节点邻居的开销(即有没有前往它们更短的路径,有则更新,具体表现见下面的例子)
3.重复1,2,直到图中每个节点都被遍历
4.计算最终路径
先介绍几个概念,像上图这样,每条边上都标有数字,这些数字称为权重,这种图称为加权图。
狄克斯特拉算法适用于有向加权无环图。无向图意味着两个节点彼此互相指向,也就是环,而绕环的路径不可能是最短的路径。
我们看一个简单的例子(书上)代码实现狄克斯特拉算法:
如图,要找出从起点start到终点end的最短路径。
思考:首先我们要像上一节一样,用数据结构把这个图存起来,按上一节的经验,我们用的是散列表的键存储节点,值存储它的邻居,但是现在还有权重需要存储,那我们就可以使用双层散列表(类似于二维数组),像下图
代码:
graph = {}
graph['start'] = {}
graph['start']['a'] = 6
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['end'] = 1
graph['b'] = {}
graph['b']['a'] = 3
graph['b']['end'] = 5
根据狄克斯特拉算法第二步,我们需要用一个散列表存储除起始点外每个节点的开销(从起点出发到该节点需要多长时间),以方便更新,代码如下:
infinity = float('inf')
costs = {}
costs['a'] = 6
costs['b'] = 2
costs['end'] = infinity
因为从起始点到终点的距离未知,所以用无穷大来暂时表示:infinity = float('inf')。
然后我们在更新开销之后,节点的父节点也会相应的改变,所以还需一个散列表来存储父节点,代码如下:
parents = {}
parents['a'] = 'start'
parents['b'] = 'start'
parents['end'] = Node
然后需要一个数组记录处理过的节点,已处理的不需要重复处理:
processsed = []
具体算法如下:
1.只要还有要处理的节点(costs中取)
2.获取离节点最近的节点
3.更新其邻居的开销
4.如果有邻居的开销被更新则同时更新它的父节点(parents)
5.将节点标记为处理过(加入processed),返回第一步
代码如下:
graph = {}
graph['start'] = {}
graph['start']['a'] = 6
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['end'] = 1
graph['b'] = {}
graph['b']['a'] = 3
graph['b']['end'] = 5
graph['end'] = {}
infinity = float('inf')
costs = {}
costs['a'] = 6
costs['b'] = 2
costs['end'] = infinity
parents = {}
parents['a'] = 'start'
parents['b'] = 'start'
parents['end'] = None
processsed = []
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 processsed:
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 new_cost < costs[n]:
costs[n] = new_cost
parents[n] = node
processsed.append(node)
node = find_lowest_cost_node(costs)
print costs['end']
补:如果加权图中有负权,则不可以用狄克斯特拉算法,要用贝尔曼-福德算法(暂不讨论)