赞
踩
注意:有向图的路径必须沿弧的方向构成顶点序列;构成路径的顶点可能重复出现(即允许反复绕圈)。
从起点开始访问所有深度遍历路径或广度优先路径,则到达终点节点的路径有多条,取其中路径权值最短的一条则为最短路径。
(1)选择单源的起点作为遍历的起始点。
(2)采用深度优先搜索或者广度优先搜索的方式遍历图,在遍历同时记录可以到达终点的路径。
(3)在所有路径中选择距离最短的路径。
采用遍历的方式获取单源最短路径,是一种暴力破解的方式。算法的性能与遍历过程性能相关。采用深度优先搜索遍历时时间复杂度为O(n+e)。
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算某个顶点到其他所有顶点的最短路径。Dijkstra(迪杰斯特拉)算法要求图中不存在负权边,即保证图中每条边的权重值为正。算法的基本思想是:从源点出发,每次选择离源点最近的一个顶点前进,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。
(1)将所有的顶点分为两部分:已知最短路程的顶点集合P和未知最短路径的顶点集合Q。最开始,已知最短路径的顶点集合P中只有源点s一个顶点。我们这里用一个book[i]数组来记录哪些点在集合P中。例如对于某个顶点i,如果book[i] = 1则表示这个顶点在集合P中,如果book[i] = 0则表示这个顶点在集合Q中。
(2)设置源点s到自己的最短路径为0即dist = 0。若存在源点有能直接到达的顶点i,则把dist[i]设为e[s][i]。同时把所有其它(即源点不能直接到达的)顶点的最短路径为设为∞。
(3)在Q中选择一个离源点s最近的顶点u(即dist[u]最小)加入到P中。并考察所有以点u为起点的边,对每一条边进行松弛操作。
(4)重复第3步,如果集合Q为空,算法结束。最终dist数组中的值就是源点到所有顶点的最短路径。
Bellman-Ford算法是从Dijkstra算法算法引申出来的,它可以解决带有负权边的最短路径问题。值得注意的是,Dijkstra算法和下面的Floyd算法是基于邻接矩阵的,而Bellman-Ford算法是基于邻接表,从边的角度考量的。用一句话概括就是:对所有的边进行n-1次松弛操作。如果图中存在最短路径(即不存在负权回路),那么最短路径所包含的边最多为n-1条,也就是不可能包含回路。因为如果存在正回路,该路径就不是最短的,而如果存在负回路,就压根就不存在所谓的最短路径。
(1)从源点到任意一点u的最短路径的长度,初始化数组dist[u]为0,其余dist[i]为无穷大。
(2)以下操作循环执行至多n-1次,n为顶点数:
对于每一条边edge(u,v),如果dist[u] + weight(u,v) < dist[v],则令dist[v] = dist[u] + weight(u,v)。若上述操作没有对dist进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;
(3)检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边edge(u,v),如果存在dist[u] + weight(u,v) < dist[v]的边,则图中存在负环路,即是说该图无法求出单源最短路径。否则数组dist[n]中记录的就是源点s到各顶点的最短路径长度。
Bellman-Ford算法初始化过程时间复杂度为O(V),对边进行了V-1趟操作,每趟操作的运行时间为O(E)。整体的时间复杂度为O(V*E)
SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它是Bellman-ford的队列优化。
(1)初始化:选取顶点u为源点,令dist[u]=0,其余赋值为INF。并将源点入队列。
(2)读取队列头的顶点,并将头顶点u出队列,将与u邻接的所有顶点v进行松弛,若v没有在队列中,则将邻接顶点v入队列。如果已经在队列中,则不再入队。
(3)队列为空时,单源最短路径查找完毕。
Floyd算法是一个经典的动态规划算法。其主要思想为:从任意顶点u到任意顶点v的最短路径不外乎2种可能,一是直接从u到v,二是从u经过若干个顶点k到v。所以,我们假设dist(u,v)为顶点u到顶点v的最短路径的距离,对于每一个顶点k,我们检查dist(u,k) + dist(k,v) < dist(u,v)是否成立,如果成立,证明从u到k再到v的路径比u直接到v的路径短,我们便设置dist(u,v) = dist(u,k) + dist(k,v),这样一来,当我们遍历完所有顶点k,dist(u,v)中记录的便是u到v的最短路径的距离。
(1)从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
(2)对于每一对顶点u和v,看看是否存在一个顶点w使得从u到w再到v比己知的路径更短。如果是更新它。
弗洛伊德(Floyd)算法的核心代码如下:
for(int i = 1; i <= n; i++)//枚举所有顶点,i代表顶点u { for(int j = 1; j <= n; j++)//枚举所有顶点,j代表顶点v { for(int k = 1; k <= n; k++)//查找是否有中间顶点w使得从u到w再到v比己知的路径更短 { if(dist[j][k] > dist[j][i] + dist[i][k]) { dist[j][k] = dist[j][i] + dist[i][k]; } } } }
可以看出Floyd算法是一种暴力破解的方式获取最短路径。Floyd算法的时间复杂度为O(n^3) 空间复杂度为O(n^2)。Floyd算法可以获得任意顶点对之间的最短路径。
最短路径问题是图论研究中的一个经典算法问题。因此针对图最短路径问题先后提出了许多算法。各类算法的应用场景不尽相同。Dijkstra算法和Bellman-Ford算法用于解决单源最短路径,而Floyd算法可以解决多源最短路径。
Dijkstra算法适用稠密图(邻接矩阵),因为稠密图问题与顶点关系密切。Bellman-Ford算法算法适用稀疏图(邻接表),因为稀疏图问题与边关系密切。 Floyd算法在稠密图(邻接矩阵)和稀疏图(邻接表)中都可以使用。
本文由 程序员小吴 创作,采用 CC BY 3.0 CN协议 进行许可,转自https://www.cxyxiaowu.com/1172.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。