07-狄克斯特拉算法

数据结构和算法
基于《算法图解》—Aditya Bhargava 和《数据结构》—严蔚敏

第7章 狄克斯特拉算法

上一章的广度优先搜索,找出的是段数最少的路径;
本章狄克斯特拉算法,找出的是最快的路径。

7.1 使用狄克斯特拉算法
步骤:
第一步:找出最便宜的节点
在这里插入图片描述

从起点出发,前往节点A需要6分钟,而前往节点B需要2分钟。置于前往其他节点,先假设为不穷大。

第二步:计算经节点B前往其各个邻居所需的时间。
在这里插入图片描述

经节点B可以找到前往节点A更短的路径,只需要5分钟。
在这里插入图片描述

对于节点B的邻居,如果找到前往它的更短路径,就更新其开销:

  • 前往节点A的更短路径(从6分钟缩短到5分钟)。
  • 前往终点的更短路径(从无穷大缩短到7分钟)。

第三步:重复

重复第一步:找出可能在最短时间内前往的节点。
已经对节点B执行了第二步,除节点B外,可在最短时间内前往的节点是节点A。

在这里插入图片描述

重复第二步:更新节点A的所有邻居节点开销。

在这里插入图片描述

可以发现前往终点的时间为6分钟。
在这里插入图片描述
在这里插入图片描述

7.1.1
使用广度优先搜索来查找两点之间的最短路径,指的是段数最少;
而在狄克斯特拉算法中,每段都分配了一个数字或权重,因此找出的是总权重最小的路径。
在这里插入图片描述

狄克斯特拉算法的4个步骤:

  • (1)找出最便宜的节点,即可在最短时间内前往的节点。
  • (2)对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。
  • (3)重复这个过程,直到对图中的每个节点都这样做了。
  • (4)计算最终路径。

7.2 术语

  • 狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。
  • 带权重的图为加权图(weighted graph),不带权重的图为非加权图(unweight graph)。
  • 环:从一个节点出发,走一圈后又回到这个节点。绕环的路径不可能是最短路径。
  • 无向图意味着两个节点彼此指向对方,其实就是环。在无向图中,每条边都是一个环。
  • 狄克斯特拉算法只适用于有向无环图(directed acyclic graph,DAG)。

7.3 换钢琴例子
Rama想用自己的乐谱来换架钢琴,那么需要确定哪种路径将乐谱换成钢琴时需要支付的额外费用最少。
在这里插入图片描述

使用狄克斯特拉算法可得到:
在这里插入图片描述

7.4 负权边
如果有负权边,就不能使用狄克斯特拉算法。
因为狄克斯特拉算法是这样假设的:对于处理过的海报节点,没有前往该节点的更短路径。这种假设仅在没有负权边时才成立。
在包含负权边的图中,要找出最短路径,可使用贝尔曼-福德算法。

7.5 实现
以下图为例。
在这里插入图片描述

要编写解决这个问题的代码,需要三个散列表。
在这里插入图片描述
算法实现思路
在这里插入图片描述

#实现图GRAPH:将节点的邻居和前往邻居的开销存储到散列表中。
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2

graph["a"] = {}
graph["a"]["fin"] = 1 #fin指到终点

graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5

graph["fin"] = {} #终点没有任何邻居

#实现图COSTS:节点的开销指的是从节点出发前往该节点需要多长时间。
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity

#实现图PARENTS:存储父节点的散列表。
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None
processed = [] #记录处理过的几点的数组,避免多次处理。

准备工作做好了,接下来实现算法。
图4.2

#首先定义函数find_lowest_cost_node找出开销最低的结点
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: #这个while循环在所有节点都被处理过后结束
	cost = cost[node]
	neighbors = graph[node]
	for n in neighbors.key(s):  #遍历当前节点的所有邻居
		new_cost = cost + neighbors[n]
		if costs[n] > new_cost: #如果经当前节点前往该邻居更近
			costs[n] = new_cost #就更新该邻居的开销
			parents[n] = node   #同时将该邻居的父节点设置为当前节点
	processed.append(node)      #将当前节点标记为处理过
	node = find_lowest_cost_node(costs) #找出接下来要处理的节点,并循环

7.6 小结

  • 广度优先搜索用于在非加权图中查找最短路径。
  • 狄克斯特拉算法用于在加权图中查找最短路径。
  • 仅当权重为正时狄克斯特拉算法才管用。
  • 如果图中包含负权边,请使用贝尔曼-福德算法。

——持续修改完善中…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值