以a为源点,求a到其他各点的最短路径、最短路径距离。
基本思想:
1. 源点一开始就被收录到集合S{ }中,其他顶点按照距离的递增原则一一被收录;
2. 每收录一个顶点v就更新一次v所相邻但未被收录的最小距离以及路径;
3. 直到所有点都被收录,算法结束。
初始化:
1. a被收录,a的最短距离为0,与a相邻的各点最短路径设为相连的边长权重,其余各点的距离为正无穷大∞;并且设置各点的最短路径,没有最短路径长度的点可设为-1
- 定义dist[ ], 则dist[a]=0, dist[b]=7, dist[e]=14, dist[f]=9, dist[c]=∞, dist[d]=∞
- 定义path[ ],则path[a]=-1, path[b]=a, path[f]=a, path[e]=a, path[d]=-1, path[c]=-1
- 注意:a, b, c...是具体的整数,可表示为顶点下标
- 以上设置的dist[ ]和path[ ]都是临时的,在算法中可能会被不断更新,直到最后为最短路径以及长度
2. 从未收录的顶点中找到距离值最小dist的那个V,然后更新该点所相邻但未被收录点的距离以及路径path(如果该收录的点没有使得相邻点距离变得更小,则不更新距离值和路径)
- 找到dist[ ]最小值,收录b;
- 其中f值dist[b]+dist<b,f>=7+10>9, 不被更新
- c点 dist[b]+dist<b,c>=7+15<∞,被更新dist[c]=dist[b]+dist<b,c>,path[c]=b
3. 重复步骤二,收录f(因为此时未收录点中dist[f]最小,看圆圈里的数字),更新e和c,此时的e、c最短距离都可以经过f点的路径变得更小。
4. 重复步骤2,收录e,更新d和对应的路径
5.重复步骤2,收录c或d(此时剩余的d和c的dist都一样,选任意一个)
6.最后收录d或c,算法结束,源点a到每个点的最短距离和路径都被计算出来
dist[V]就是源点到任意点V的最短距离
回溯path[V],path[path[V]],path[path[path[V]]]......直到path[a]=-1可以用堆栈顺序地将源点到任意点V的路径输出
时间复杂度分析:
方法一:如果每次收录最小dist是遍历所有的未收录V,则共遍历|V|次,|V|个点被遍历|V|遍则O(|V|^2),而每条边在收录过程中被访问一次O(|E|),加起来就是
O(|V|^2+|E|
)。
方法二:如果我们把dist储存到“最小堆”里面(最小堆的建立最小时间复杂度是O(|V|),即线性时间),每次取出最小值,将最小堆调整一次O(log|V|),而且每次取出这个最小值之后要更新有相邻边顶点的值,即对最小堆里面个别值进行更新并且调整O(log|V|),综合起来就是
O(|V|log|V|+|E|log|V|)
。
分析:对于稠密图来说,O(|E|)=O(|V|^2),则用方法一比较好;对于稀疏图来说,O(|E|)=O(|V|)用方法二比较好。以上这两种方法的区别就在于,第一种方法最大消耗时间在检索最小距离点的时候是遍历过程,而在更新时候只是需要搜索部分边(所以边一般只遍历了线性时间),而第二种方法最大消耗时间在于更新相邻边的顶点值,而且要更新该顶点在最小堆里面的位置,一共是:边数×更新时间=|E|log|V|。
主要参考:MOOC课程-浙江大学陈越-数据结构与算法