深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)

这里需要一个很好的例子,这里先拿网上流传的例子作为反例。


深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)_第1张图片


问题1:不符合勾股定理

AC=3,CB=2,AB=6

难道这样真的无伤大雅吗?假设你的起点是A,终点是B,难道不应该是两点之间线段最短吗?、


问题2:完美地扫到了每个点

按照他的思维逻辑来,确实每个点都可以扫到,但是事实上,我们应该充分考虑没扫到的情况之后是怎样的,这样才能真正理解Dijkstra算法。


深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)_第2张图片


这个图会很容易发现,按照Dijkstra的算法,当前点走动的方向是如图所示的。但是走到E点会发现此路不通,因为没被访问的点剩下了F,G,但是他们之间又是无穷远的。如果读者是这样想的,那就进入思维误区了。


正确思考:(设U是未走到过的集合,初始U={A,B,C,D,E,F,G})

第一次,BCDEFG都是无穷远,设为1000好了,发现A和F、B是相连的,所以用A去更新F、B。

深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)_第3张图片

第二次,到达B,U中去除B,剩下CDEFG。到了B,发现DC是可达的,更新DC。发现D更短,去往D。

深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)_第4张图片

第三次,到达D,把D从U中去除,剩下CEFG。与D相连的有EC(另外的已访问过所以不再考虑),这里得出E为10,小于1000(无穷远),可以被更新。但是看向C点,却发现9>7.5更新不了。那么A到C的最短路径依然是ABC而不是ABDC。

深入理解Dijkstra(迪杰斯特拉)算法(正确思路+优化+原理)_第5张图片

到达E,这个时候U中剩下F,G。发现7.5+4大于10,还是更新不了,所以A到E还是ABDE最短。

这里就回到了我们之前的关键点了。走Dijkstra算法,不是靠走的,而是靠从U集合中取的!

所以这里取出的是F,成功使用F更新了G,G被更新为7,FD 6+5>7更新不了。

其实A你也可以认为是0。


此外,刚才选取的时候,我们怎么知道应该选取F而不是G?假设我们选的是G,但是他自己没有被更新过,所以他也不能更新另外的值。所以我们需要加一个判断,当前值是被更新过的才能去更新另外的值。换个角度,不加判断其实也行。因为如果你取到了一个没有被更新过的数,那么就是1000,1000去更新另外的,只有可能比1000要大,所以他更新谁都不会成功,如果你加个判断,等于是进行了一点点的优化。


梳理下流程,不断从U中去取,然后不断更新,直到U中被取完,这样我们就得出了起点到达每个点的最短距离。

但是最短路径怎么求呢?之前我们到达E点断掉了,说明靠走肯定不行,说明直接求最短路径也肯定求不出来。所以我们和之前一样,我们怎么把最短路径长度维护给每个点的,就怎么把最短路径交给每个点去维护。我们需要开一个二维数组,以更新路径的形式即可。


优化:采用优先队列去优化。意思就是当你在A的时候,发现AB=3.5,AF=6,另外的1000,都更新他们,再把他们都丢进优先队列,而优先队列会自动替你把这一串,由小到大排好序。所以你只需要取队列的队首元素就可以了,省去了你找到最小值的过程。我们使用优先队列就是节省了排序的时间。此外你可以用Vector或者邻接表,不用像二维数组一样开一个矩阵,会更快。


原理:也就是为什么我们要选取U中的最小值来更新而不是任意一个大小的值?因为只有这样,你才能保证你每个点都能被更新成最小值。假设之前你用一个较大的值而没有用最小值去更新,那么就之后就可能会出现,你这个较大的值后来被更新了,但是一开始被你这个较大的值所更新的值并没有被更新到。具体举个例子,看最后一幅图,假如我们一开始取的是F点,那么我们就会把G点更新成7,那么假如之后,发现一条更短的路径到F,我们F点会被更新,但是G点却不会被更新了啊,所以数据就失真了。那么为什么我们从最小的点开始取不会出现这个情况呢?举个例子,我们根据选取最小值来选,AB=3.5,AF=6。那么自然是选AB。然后由B去更新后面的值。这里我们思考一下上面的那个问题,有没有可能发现一条到达B更短的路,从而使得被B更新的最小值全部失效了?肯定就没有了。那为什么上面那个情况有?其实上面那个例子举的不太好,不过讲到这里大家应该也都能明白是为什么了。

你可能感兴趣的:(算法知识)