摘要
- Dijkstras算法的基本原理和求解步骤
- Dijkstras算法用python实现的思路和源代码
- Dijkstras算法的适用范围
代码和笔记记录下载请到我的github仓库。
Dijkstras算法与实现
基本原理
- 广度优先搜索,适用于非加权图(unweighted graph),找到的最短路径是段数最少的路径。迪克斯特拉(Dijkstras)算法,适用于加权图(weighted graph),找出的是总权重最小的路径。
- Dijsktra算法包含四个步骤:
- 找出当前离起点最近的节点;
- 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销,并更新邻居的父节点;
- 重复这个过程,直到对所有节点都这样做了;
- 计算最终路径(路线和开销)。
- Dijstras的基本思想:对于处理的节点,已经找到到它们的最短路径(或没有到达它们的更短路径)。
以下图题目为例:
)
利用算法求最短路径的求解过程如下:
于是最终得到的最短路径
start--> B--> A--> fin
,开销为6。
算法实现
- 节点的开销应初始化为无穷大,保证只要有路线可达,其开销一定能被更新。Python中是
float('inf')
。 - 加权有向图仍然是散列表实现。
- 求解全过程要用到:
- 有向图散列表;
- 开销散列表;
- 父节点散列表;
- 待处理节点列表(或已处理节点列表)。
- 对
fin
终点不需要进行Dijkstras算法。
适用范围
- 首先,Dijkstras算法适用于有向加权图。
- 对于有向无环图(directed acyclic graph, DAG)也是适用的,因为Dijkstras算法会自动淘汰含有环的边。
- 不适用含负权边的有向图,负权边会在节点检查过之后仍然更新其开销,不符合Dijkstras算法的基本思想。在包含负权边的图中找最短路径要用另一种算法——Bellman-Ford算法。
代码实现(python)
针对上面的有向图求解。
代码实现和笔记都在我的github仓库可以下载到。
from pprint import pprint
def find_lowest_cost(costs,to_process):
lowest_cost_node = to_process[0]
lowest_cost = costs[lowest_cost_node]
if len(to_process)>1:
for node in to_process[1:]:
new_cost = costs[node]
if new_cost < lowest_cost:
lowest_cost = new_cost
lowest_cost_node = node
return lowest_cost_node
def set_up_graph():
graph = {}
graph['start'] = {"A":6,"B":2}
graph["A"] = {'fin':1}
graph["B"] = {"A":3,"fin":5}
graph['fin'] = {}
return graph
def initialize_costs_n_fathers(graph):
costs, fathers ={},{}
for node in graph:
if node is "start":
costs[node] = 0
fathers[node] = None
else:
costs[node] = float('inf')
return costs,fathers
def get_shortest_path(fathers):
path = []
father = 'fin'
path.append(father)
while father != 'start':
father = fathers[father]
path.append(father)
print 'Shortest path is:',
for i in path[-1:-len(path):-1]:
print '{}-->'.format(i),
print path[0]
def main():
graph = set_up_graph()
print 'Graph as below:'
pprint(graph)
print '\n'
costs, fathers = initialize_costs_n_fathers(graph)
to_process = [i for i in graph.keys()]
to_process.remove('fin')
while to_process:
node = find_lowest_cost(costs,to_process)
neighbors = graph[node]
for neighbor in neighbors:
new_cost = costs[node] + graph[node][neighbor]
if new_cost < costs[neighbor]:
costs[neighbor] = new_cost
fathers[neighbor] = node
to_process.remove(node) # keys donnot share names
get_shortest_path(fathers)
print 'The lowest cost is {}.'.format(costs['fin'])
if __name__=='__main__':
main()
运行结果为:
Graph as below:
{'A': {'fin': 1},
'B': {'A': 3, 'fin': 5},
'fin': {},
'start': {'A': 6, 'B': 2}}
Shortest path is: start--> B--> A--> fin
The lowest cost is 6.
也可以将上面的图改成是有环的图,就能验证到对有环图也是适用的。