Dijkstra算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
下面,我用python来举例详细介绍这个算法
问题如下:
如图所示(我们规定箭头上的数字为时间)现有如下路径图,我们想尽快从起点到达终点,那也就是求最快路径的问题了
接下来我们先把问题建模
这是GRAPH表,将该路径描述用表格形式表现出来
graph = {}#定义邻居表
graph["start"] = {}#起点的邻居
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}#a点的邻居
graph["a"]["end"] = 1
graph["b"] = {}#b点的邻居
graph["b"]["a"] = 3
graph["b"]["end"] = 5
graph["end"] = {}#终点没有任何邻居
然后是COSTS表,用于将起点到各个点的的开销列出来,不能直接到达的,该事件设为无穷大(用“-”表示,后期再更改)
costs = {}#定义开销表,即起点到其余节点的开销,不是直接到达的,就暂时用无穷大定义
costs["a"] = 6
costs["b"] = 2
costs["end"] = float("inf")#定义极大值
最后是PARENT表,用于记录每个节点的父节点(父节点:如B被起点指向,则称起点是B的父节点)(若有某个远端节点(非起点指向)被两个节点指向,则暂时不写,用“-”表示)
parent = {}#定义父节点表,仅看只有一个父节点的节点,若有两个父节点,则暂时定义为空
parent["a"] = "start"
parent["b"] = "start"
parent["end"] = None #终点节点(非远端节点)在表中有两个父节点,故暂时不填
至此,问题描述,建模完毕
我们先定义一个列表,用于存储那些被处理的节点,防止重复处理
processed = [] #定义flag,用于标记某节点已被处理,防止多余计算甚至死循环
接下来是具体算法描述部分:
定义find_lowest_cost_node函数,传入COSTS表,用于查找当前未处理的,函数返回用时最少的下一节点,通俗点来说,就是把起点的子节点按用时情况,从小到达依次返回一个子节点(是返回一个节点,不是时间),思想是:每次定义一个无穷大,然后每次将起点的子节点遍历,直到找出此时的用时最小的节点,思想类似冒泡排序中的找出最大/小值
#此函数的作用是依次获取该起点能到的所有点,从小到大
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
最关键的下面的算法思想:
上一个函数将依次返回起点到另一个用时最少的节点,然后分如下几步:
1.记录起点到该点的用时cost
2.遍历该节点的所有邻居,分别得到该点到其各个邻居的用时(比如B的一个邻居A)
3.在原始costs表中查询到起点到A的开销为6,就是代码中的costs[n],起点到B的开销为2(也就是步骤1中cost)B到A的开销为3,就是代码中的neighbors[n],将costs[A]与cost+neighbors[A]作比较,也就是(6与2+3作比较),后者小于前者,那么就更新路径(之前是起点直接到A,用时6,现在要想到达A,就起点先到B,然后B再到A,用时5,显然后面这条路径用时少一些,然后更新costs表和parent表)
def main():
node = find_lowest_cost_node(costs)
while node is not None:
cost = costs[node]
neighbors = graph[node]#获取该node节点的邻居
for n in neighbors.keys():#将该节点的邻居依次遍历出来
new_cost = cost+neighbors[n]#计算从当前节点到达其邻居的开销与其起点到其开销的和
if costs[n] > new_cost:#如果新开销和比之前的开销小,就更新开销表和父节点表
costs[n] = new_cost#将起点到该节点的开销更新
parent[n] = node#更新该节点的父节点,意味着从这条路径过来花的时间最少
processed.append(node)#将该节点标记为已处理节点
node = find_lowest_cost_node(costs)#继续找下一个未被处理的节点
print(costs["end"])
弗洛伊德算法:Floyd-傻子也能看懂的弗洛伊德算法(转)