回顾:上次我们说到了狄克斯特拉算法用于每条边都有关联的图,这些数字称为权重。
新概念:环,图中可能有环,而环类似下图这样。
环意味着你可从一个节点出发,走一圈后又回到这个节点。假设在下面这个带坏的的图中,你要找出从起点到终点的最终路径。
图:我们发现从起点到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的吉他或架子鼓。
现在我们知道,我们的朋友只要再花一点点钱,R就能拿乐谱换架钢琴。现在R要确定的是,如何花最少的钱实现这个目标,我们来绘制一个图,列出大家的交换意愿。
这个图中的节点是大家愿意拿出来交换的东西,边的权重是交换时需要额外加多少钱。R需要确定采用哪种路径将乐谱换成钢琴时需要支付的额外费用最少。因此,可以使用狄克斯特拉算法,在这个例子中,你将完成所有这些步骤,因此你也将计算最终路径。
动手之前,我们先用一个表格列出其中每个节点的开销,这里的开销指的是达到节点需要额外支付多少钱。
表格:
黑胶唱片:5
海报:0
吉他:∞
架子鼓:∞
钢琴:∞
因为我们还不知道如何从起点前往这些节点:我们还没有更新
在执行狄克斯特拉算法的过程中,我们将不断更新这个表。为计算最终路径,还需在这个表中添加表示父节点的列。父节点的意思是该节点的前一个节点
节点:父节点
黑胶唱片:乐谱
海报:乐谱
吉他:未知
架子鼓:未知
钢琴:未知
第一步:找出最便宜的节点。在这里,换海报最便宜。还有更便宜的换海报的途径吗?
这一点非常重要,我们来想一想,我们的R能够通过一系列交换得到海报,还能额外得到钱吗?答案是不能,因为海报是R能够到达的最便宜的节点,没法再便宜了。下面我们再举个例子,假设你要从家里去单位。
如果经停车场前往学校,能不能将时间缩短到少于2分钟呢?不可能,因为只前往停车场就需要6分钟。另一方面。有没有更快到达停车场的路呢?有。
这就是狄克斯特拉算法背后的理念:找出图中最便宜的节点,并确保没有到该节点的更便宜的路径!
回到换钢琴的例子。换海报需要支付的额外费用最少。
第二步:计算前往该节点的各个邻居的开销。
这里可以理解为:父节点换节点的开销
父节点:节点:开销
乐谱:黑胶唱片:5元
乐谱:海报:0
海报:吉他:30
海报:架子鼓:35
未知:钢琴:无穷大
因为我们钢琴的父节点还没有找到,为了便于对比其他交换例子的开销,所以这里用无穷大
现在的表中包含低音吉他和架子鼓的开销。这些开销是用海报交换他们时需要支付的额外额外费用,因此父节点为海报。这意味着,要到达低音吉他,需要沿从海报出发的边前行,对架子鼓来说亦是如此。
再次执行第一步:下一个最便宜的节点是黑胶唱片————需要额外支付5美元。
再次执行第二步:更新黑胶唱片的各个邻居的开销:
你更新了架子鼓和吉他的开销!这意味着经“黑胶唱片”前往“架子鼓和“吉他”开销更低,因此你将这些乐器的父节点改为黑胶唱片。
父节点:节点:开销
乐谱:黑胶唱片:5元
乐谱:海报:0
黑胶唱片:吉他:20
黑胶唱片:架子鼓:25
未知:钢琴:无穷大
下一个更便宜的是吉他,因此更新其邻居的开销。
父节点:节点:开销
乐谱:黑胶唱片:5元
乐谱:海报:0
海报:吉他:30
海报:架子鼓:35
吉他:钢琴:40
终于计算除了用吉他换钢琴的开销,将父节点设置为吉他。最后,对最后一个节点——架子鼓做同样的处理。
父节点:节点:开销
乐谱:黑胶唱片:5元
乐谱:海报:0
海报:吉他:30
海报:架子鼓:35
架子鼓:钢琴:35
如果用架子鼓换钢琴,R需要额外支付的费用更少。因此,采用最便宜的交换路径时,R需要额外支付35美元。现在我们来确定最终的路径。当前,我们知道最短路径的开销为35美元,但如何确定这条路径呢?为此,先中出钢琴的父节点。
钢琴的父节点为架子鼓,这意味着R需要用架子鼓来换钢琴因此我们就沿着这一边。
我们来看看需要沿哪些边前行。钢琴的父节点为架子鼓。
架子鼓的父节点为黑胶唱片。
因此R需要用黑胶唱片来换架子鼓,显然,他需要用乐谱来换黑胶唱片。通过沿父节点回溯,便得到了完整的交换路径。:乐谱花5元换黑胶唱片,用黑胶唱片花20元换架子鼓,最后用架子鼓花10元换钢琴。
新概念:负权边:
在前边的交换实例中,A提供了两种可用乐谱交换的东西。
假设黑胶唱片不是A的,而是S的,且S愿意用黑胶唱片和7美元换海报。换句话说,换得A的海报后,R用它来换S的黑胶唱片时,不但不用支付额外的费用,还可得7美元。对于这种情况,如何在图中表示出来呢?
从黑胶唱片到海报的边为负!现在R有两种获得海报的方式。
第二种方式更划算---R可以赚2美元!你可能还记得,R可以用海报换架子鼓,但现在有两种换的架子鼓的方式。
1.R可以用乐谱换黑胶唱片再用黑胶唱片换海报再用海报换架子鼓---开销33元
2.R可以用乐谱换海报再用海报换架子鼓--开销35元。
如果你用狄克斯特拉算法来计算这个图,R会选择错误的路径,开销最多的那条,让我们来看看对这个图执行狄克斯特拉算法的情况。
首先,看看开销表:
黑胶唱片:5
海报:0
架子鼓:无穷大(再说一遍,因为没有更新它的父节点)
接下来,找出开销最低的节点,并更新其邻居的开销。在这里,开销最低的节点是海报,我们来更新其邻居的开销
黑胶唱片:5
海报:0
架子鼓:35(此时架子鼓的父节点是海报)
现在架子鼓的开销变成了35
我们来找出最便宜的未处理的节点(目前只有两个父节点:黑胶唱片和海报,但是海报已经处理了,所以现在我们来处理黑胶唱片)##记住!这里海报的节点已经更新了!!
更新黑胶唱片的节点:唯一的邻居——海报已经被处理了,也就是在计算机里已经把海报这个邻居删除了,所以我们发现黑胶唱片没有邻居,因此算法到此结束,开销如下:
换的架子鼓的开销为35。你知道有一种交换方式只需要33,但狄克斯特拉算法没有找到。这是因为狄克斯特拉算法这样假设:对于处理过的海报节点,没有前往该节点的更短路径。这种假设仅在没有负权边时才成立。因此,不能将狄克斯特拉算法用于包含负权边的图。
那么本期先到这里 下一期用Python实现狄克斯特拉算法
感谢大家的阅读 谢谢大家!