python 父路径_小海豹教你算法,包你懂:Python实现狄克斯特拉算法详解(2)

回顾:上次我们说到了狄克斯特拉算法用于每条边都有关联的图,这些数字称为权重。

新概念:环,图中可能有环,而环类似下图这样。

f0d50aba5a9009f8755668d79cf31d25.png

环意味着你可从一个节点出发,走一圈后又回到这个节点。假设在下面这个带坏的的图中,你要找出从起点到终点的最终路径。

图:我们发现从起点到B的耗时是2h,从B到终点的耗时是3h,而A和B是一个环,这意味着:你可以从A到B,也可以从B到A,耗时都为4h

思路:我们可以避开环的路径:从起点到B,再从B到终点,总耗时5h

我们也可以选择包含环的路径:从起点到B,再从B到A,再从A到B,最后从B到终点。总权重(耗时)13h

如果你愿意,我们甚至可以绕两次环,总权重为21h

但没绕环一次,总权重都增加8.因此,绕环的路径不可能是最短的路径。

这里要注意狄克斯特拉算法只适用于有向无环图·

换钢琴:

术语介绍的差不多了,我们再来看一个例子:我们的朋友R,想拿一本乐谱换架钢琴。A说:我愿意用海报换乐谱,如果R再加5元,还可以拿乐谱换R的唱片。M说:我愿意拿我的吉他和架子鼓换这张海报和黑胶唱片。B说:我愿意那我的钢琴换M的吉他或架子鼓。

eab07108ef229a757f058574914b4c0f.png

现在我们知道,我们的朋友只要再花一点点钱,R就能拿乐谱换架钢琴。现在R要确定的是,如何花最少的钱实现这个目标,我们来绘制一个图,列出大家的交换意愿。

这个图中的节点是大家愿意拿出来交换的东西,边的权重是交换时需要额外加多少钱。R需要确定采用哪种路径将乐谱换成钢琴时需要支付的额外费用最少。因此,可以使用狄克斯特拉算法,在这个例子中,你将完成所有这些步骤,因此你也将计算最终路径。

动手之前,我们先用一个表格列出其中每个节点的开销,这里的开销指的是达到节点需要额外支付多少钱。

表格:

黑胶唱片:5

海报:0

吉他:∞

架子鼓:∞

钢琴:∞

因为我们还不知道如何从起点前往这些节点:我们还没有更新

在执行狄克斯特拉算法的过程中,我们将不断更新这个表。为计算最终路径,还需在这个表中添加表示父节点的列。父节点的意思是该节点的前一个节点

节点:父节点

黑胶唱片:乐谱

海报:乐谱

吉他:未知

架子鼓:未知

钢琴:未知

第一步:找出最便宜的节点。在这里,换海报最便宜。还有更便宜的换海报的途径吗?

8cffacf3dafa5c9ab3503d09c04cc40b.png

这一点非常重要,我们来想一想,我们的R能够通过一系列交换得到海报,还能额外得到钱吗?答案是不能,因为海报是R能够到达的最便宜的节点,没法再便宜了。下面我们再举个例子,假设你要从家里去单位。

7a5588ee0fa908bb7e264d2104513e21.png

如果经停车场前往学校,能不能将时间缩短到少于2分钟呢?不可能,因为只前往停车场就需要6分钟。另一方面。有没有更快到达停车场的路呢?有。

这就是狄克斯特拉算法背后的理念:找出图中最便宜的节点,并确保没有到该节点的更便宜的路径!

回到换钢琴的例子。换海报需要支付的额外费用最少。

第二步:计算前往该节点的各个邻居的开销。

这里可以理解为:父节点换节点的开销

父节点:节点:开销

乐谱:黑胶唱片:5元

乐谱:海报:0

海报:吉他:30

海报:架子鼓:35

未知:钢琴:无穷大

因为我们钢琴的父节点还没有找到,为了便于对比其他交换例子的开销,所以这里用无穷大

现在的表中包含低音吉他和架子鼓的开销。这些开销是用海报交换他们时需要支付的额外额外费用,因此父节点为海报。这意味着,要到达低音吉他,需要沿从海报出发的边前行,对架子鼓来说亦是如此。

再次执行第一步:下一个最便宜的节点是黑胶唱片————需要额外支付5美元。

再次执行第二步:更新黑胶唱片的各个邻居的开销:

9dd3fb10461432917e776a629cf19b20.png

你更新了架子鼓和吉他的开销!这意味着经“黑胶唱片”前往“架子鼓和“吉他”开销更低,因此你将这些乐器的父节点改为黑胶唱片。

父节点:节点:开销

乐谱:黑胶唱片:5元

乐谱:海报:0

黑胶唱片:吉他:20

黑胶唱片:架子鼓:25

未知:钢琴:无穷大

下一个更便宜的是吉他,因此更新其邻居的开销。

父节点:节点:开销

乐谱:黑胶唱片:5元

乐谱:海报:0

海报:吉他:30

海报:架子鼓:35

吉他:钢琴:40

终于计算除了用吉他换钢琴的开销,将父节点设置为吉他。最后,对最后一个节点——架子鼓做同样的处理。

父节点:节点:开销

乐谱:黑胶唱片:5元

乐谱:海报:0

海报:吉他:30

海报:架子鼓:35

架子鼓:钢琴:35

如果用架子鼓换钢琴,R需要额外支付的费用更少。因此,采用最便宜的交换路径时,R需要额外支付35美元。现在我们来确定最终的路径。当前,我们知道最短路径的开销为35美元,但如何确定这条路径呢?为此,先中出钢琴的父节点。

钢琴的父节点为架子鼓,这意味着R需要用架子鼓来换钢琴因此我们就沿着这一边。

我们来看看需要沿哪些边前行。钢琴的父节点为架子鼓。

架子鼓的父节点为黑胶唱片。

因此R需要用黑胶唱片来换架子鼓,显然,他需要用乐谱来换黑胶唱片。通过沿父节点回溯,便得到了完整的交换路径。:乐谱花5元换黑胶唱片,用黑胶唱片花20元换架子鼓,最后用架子鼓花10元换钢琴。

新概念:负权边:

在前边的交换实例中,A提供了两种可用乐谱交换的东西。

2067e9f11c23370fa3f7395871be734c.png

假设黑胶唱片不是A的,而是S的,且S愿意用黑胶唱片和7美元换海报。换句话说,换得A的海报后,R用它来换S的黑胶唱片时,不但不用支付额外的费用,还可得7美元。对于这种情况,如何在图中表示出来呢?

314b63bdf3c5525c963983ee7b8aa4fa.png

从黑胶唱片到海报的边为负!现在R有两种获得海报的方式。

第二种方式更划算---R可以赚2美元!你可能还记得,R可以用海报换架子鼓,但现在有两种换的架子鼓的方式。

1.R可以用乐谱换黑胶唱片再用黑胶唱片换海报再用海报换架子鼓---开销33元

2.R可以用乐谱换海报再用海报换架子鼓--开销35元。

如果你用狄克斯特拉算法来计算这个图,R会选择错误的路径,开销最多的那条,让我们来看看对这个图执行狄克斯特拉算法的情况。

首先,看看开销表:

黑胶唱片:5

海报:0

架子鼓:无穷大(再说一遍,因为没有更新它的父节点)

接下来,找出开销最低的节点,并更新其邻居的开销。在这里,开销最低的节点是海报,我们来更新其邻居的开销

黑胶唱片:5

海报:0

架子鼓:35(此时架子鼓的父节点是海报)

现在架子鼓的开销变成了35

我们来找出最便宜的未处理的节点(目前只有两个父节点:黑胶唱片和海报,但是海报已经处理了,所以现在我们来处理黑胶唱片)##记住!这里海报的节点已经更新了!!

更新黑胶唱片的节点:唯一的邻居——海报已经被处理了,也就是在计算机里已经把海报这个邻居删除了,所以我们发现黑胶唱片没有邻居,因此算法到此结束,开销如下:

换的架子鼓的开销为35。你知道有一种交换方式只需要33,但狄克斯特拉算法没有找到。这是因为狄克斯特拉算法这样假设:对于处理过的海报节点,没有前往该节点的更短路径。这种假设仅在没有负权边时才成立。因此,不能将狄克斯特拉算法用于包含负权边的图。

那么本期先到这里 下一期用Python实现狄克斯特拉算法

感谢大家的阅读 谢谢大家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值