迪杰斯特拉算法用于计算:某点v0到其他所有点的最短路径,时间复杂度为O(n^2)
初态:
设定V为所有顶点的集合。
设定S为已经得到的最短路径的顶点vi的集合。即S中的顶点vi,都是已经确定下来v0到vi间的最短距离的顶点的集合。
设定V-S为尚未计算得到最短路径的顶点集合。
初态下,S为空,V-S=V。
D[vi]为v0到vi的最短距离。
基本思路:
在V-S中,找一个顶点vi,该vi要满足:v0->vi间的距离,是V-S中v0到其他顶点vj的距离中最小的顶点。
现在考虑v0->vi的最短路径,这个最短路径有三种可能:
1).v0直接到vi
2).v0到S中的某个或某多个顶点vk,在经由vk回到vi
3).v0经过V-S中的某个或多个顶点vj,在经由vj回到vi.
首先考虑3),这是不可能的。因为前提是v0->vi是V-S中最短的距离,如果说v0->vj->vi的距离是最短的,则v0->vj的距离一定小于v0->vi(因为v0->vi=v0->vj+vj->vi),而前提是假设v0->vi已经是V-S中最短的了,即v0->vj一定是大于v0->vi的,这与3)矛盾,因此只剩下1)和2)两种可能。
因此,求v0->vi的最短距离,只要考虑1)与2)即可。
基本流程:
1).在V-S中找到一个满足v0->vi最短的顶点vi.
2).更新v0经由vi到V-S中其他各点的最短距离,即如果k属于V-S且v0->vi->vk,则更新D[vk]=D[vi]+vi->vk。
循环1)与2) n-1次后,任意点vi的D[vi]即为v0->vi的最短距离长度。
不是问题的问题:
看起来在基本流程的1)与2)的过程中,没有体现出基本思路中1)vo直接到vi与2)v0到S中的某个或某多个顶点vk,在经由vk回到vi间的比较,但实际上这个比较已经发生了。
考虑正在计算v0->vi的最短路径,设定vk为计算v0->vi前的某个顶点,该顶点vk已经计算了v0->vk的最短距离并将加入到了S中。
在计算vk的过程中,在进行到基本流程的2)时:
此时开始更新v0经由vk到V-S中各点的距离,当遇到vi时,此时vi尚在V-S中,计算发现v0->vk->vi<v0->vi,则更新D[vi]=D[vk]+vk->vi。
实际上在计算v0到vi的最短路径时,基本思路中1)与2)的对比是发生在vk的,在计算vk的第二个步骤中,发现v0->vk->vi的距离小于v0->vi,并更新了D[vi],也就是说,v0->vi和v0->经由S中的顶点->vi的距离谁更短这个问题已经在计算vi之前的顶点中发生了,当计算到vi时,此时的D[vi]已经代表了v0->vi和v0->经由S中的顶点->vi中的最小值。
佛洛依德算法:
用于计算每对顶点间的最短路径。其实使用迪杰斯特拉算法也可以达到每对顶点间的最短路径,只要对每个顶点均进行一次迪杰斯特拉,复杂度为O(n^3)。佛洛依德算法的复杂度也为O(n^3).
基本思路:
计算某两点vi到vj的距离时,首先考虑vi->vj的直接路径,这个路径(如果存在的话)可能是最短路径,也可能不是。
现在在这两个路径间加入一个中间节点v0,变为vi->v0->vj,这个也可能是最短路径(如果存在的话),与上面的vi->vj对比一下,得到的路径就是在中间节点为v0下的最短路径。
然后再往里加入一个节点vk,现在相当于有两个中间节点v0与vk了,vi经由v0与vk到vj的路径也可能是真正的最短路径。此时首先找出v0->vk间的最短路径,这个最短路径的中间节点目前只能有v0,或者没有,也就是对比vi->vk与vi->0->vk,得到的路径就是vi->vk的最短路径。同样对于vk->vj,在只用v0或不用的情况下,找出vk->vj的最短路径。然后将vi->vk与vk->vj这两个刚更新的最短路径加起来后与未加入vk前vi->vj间的最短路径作比较,最小的就是在中间节点为v0,vk的情况下的最短路径。
循环上面的过程,知道所有中间节点都已加入。
实际上这个过程就是,首先只有起点vi与终点vj,然后不断加入中间节点,计算得到在这些中间节点的情况下的最短路径,每次加入一个中间节点,都是相当于在不断逼近完整的图,在所有中间节点都已经加入后,这种情况已经是完整的图下求vi与vj间最短路径的问题了。
基本流程:
1).加入第idx个中间节点(初始情况下idx=0,也就是直接vi->vj)。
2).在只使用前idx-1个中间节点的情况下,更新vi->vidx(vidx是刚加入的中间节点)与vidx->vj这两个顶点间的最短路径。
3).将未加入第idx前vi->vj的最短路径与加入后vi->vidx+vidx->vj的距离进行对比,较小的作为加入第idx个中间节点的情况下vi到vj的最短距离。
一点不是问题的问题:
注意,如果说求的是整个图中任意两点间的最短距离,则在2)步中,通过两个for循环(严P191中v与w两个循环)已经更新了vi到任意某个尚未加入到图中的中间节点vm的距离与vm到vj的距离,这样当在将vm加入到图中时,D[vi][vm]和D[vm][vj]已经是最短的了。
而如果求的是固定两个点vi和vj的最短距离,则在某个中间节点vz加入图中后,在进行到2)时,还要更新vi经由vz到所有尚未加入图的中间节点的距离,比如某个节点vk,这个节点要在vz后才加入,现在正在处理vz到2),此时比较vi->vk与vi->vk->vz的距离,更新D[vi][vk],这样当进行到加入vk时,D[vi][vk]就直接代表了vi到vk的最短距离。同理vk->vj也是如此,也是在计算vz的2)时,对比vk->vj与vk->vz->vj的距离,更新D[vk][vj],这样在加入vk到中间节点时,就能直接对比vk加入前的最短距离与D[vi][vk]+D[vk][vj]的大小。