不一样性质的图中,所采起的策略有所不一样,天然存在各样的求最短路径的算法。算法
对于无向无权图(也能够假设权值为1),就可使用最基本的广度优先搜索算法,从源点开始对整个图进行搜索,访问到全部的点。由于广度优先搜索最早访问到的是相邻的点,因此距离最近的点最早访问到,记录的距离也就最小。code
算法伪代码,时间复杂度为 $O(|V|+|E|)$。队列
for all u in V: dist(u) = INF dist(s) = 0 Q = [s] (FIFO 队列) while Q is not empty: u = eject(Q) for all edges (u,v) in E: if dist(v) = INF: inject(Q, v) dist(v) = dist(v)+1
Dijkstra 算法是基于广度搜索的单源最短路径算法,利用此算法能够在无负边的有向图中求得一点到图中其余点的最短路径。算法的基本思想是每一个点记录源点到其余点的距离,在从以按距离值大小排列的优先队列中选取值最小的点,再更新相邻点的距离值,直到队列为空。get
假设求点 s 到图中其余点的最短路径,$dist(u)$ 表示 s 到 u 的距离。 H 表示优先队列。
Dijkstra 算法伪代码表示以下it
for all u in V: dist(u) = INF prev(u) = None dist(s) = 0 H = makequeue(V) (using dist-values as keys) while H is not empty: u = deletemin(H) for all edges (u, v) in E: if dist(v) > dist(u) + l(u,v): dist(v) = dist(u) + l(u,v) prev(v) = u decreasekey(H, v)
算法总共涉及 $|V|$ 次队列删除操做,$|V|+|E|$ 次队列插入和更新操做。若是优先队列的实现是基于二叉堆(binary heap)则队列插入和删除操做时间算法复杂度都为 $O(log|V|)$,算法的时间复杂度为 $O((|V|+|E|)log|V|)$。扩展
Bellman-Ford 算法跟 Dijkstra 算法同样都是单源最短路径算法,可是能够应用于存在负边的有向图中。Dijkstra 算法中最关键的步骤就是当前距离值最小的点根据到相邻的点距离,更新相邻点到源点的距离,被选出的距离值最小的点的距离值是成递增关系。若是存在负边则前面已经被选为距离值最小的点的值可能改变,变得更小,也就使得 Dijkstra 算法不适用。sed
Dijkstra 算法的关键步骤是更新点到源点的距离值,更新按照距离值递增的顺序。若是存在负边就不能确保仍然按照这种递增的顺序更新,但最终还是更新路径上全部点的距离值直到再也不改变。一条最短路径最多通过|V|-1条边,因此 Bellman-ford 经过|V|-1次重复更新全部的边,来确保更新是按照正确的顺序进行。搜索
若是图中不存在负环,则|V|-1次更新后,全部点的距离值都达到最小,不会再改变。存在负环的话,再进行一次更新,有的点的距离值仍然会改变。经过这种方法也能够判断图中是否存在负环。queue
算法伪代码以下,时间复杂度 $O(|V|*|E|)$方法
for all u in V: dist(u) = INF prev(u) = None dist(s) = 0 repeat |V|-1 times: for all (u,v) in E: dist(v) = min{dist(v), dist(u)+l(u,v)}
对于一个有向无环图,能够经过深度优先搜索得到其线性化顺序(linearised order),以下图
dist(u)表示从源点 s 到 u 的最短距离。求上图中的D点 的dist(D),则只要知道跟 D 相邻并指向 D 的点的距离值(dist(B), dist(C)),经过比较取最小值:
$$ dist(D) = min\lbrace{dist(B)+1, dist(C)+3}\rbrace$$
要求获得某个点的最短距离前,要先求获得全部指向该点的最短距离。求解一个问题前,先要解决多个子问题,这也就利用了动态规范方法。
算法的伪代码以下,时间复杂度$O(|V|+|E|)$(DFS 得到线性化顺序的时间复杂度)。
for all u in V: dist(u) = INF dist(s) = 0 LV = DFS(s) # linearised order for v in LV: dist(v) = min{ dist(u)+l(u,v) for (u,v) in E }
Floyd-Warshall 算法是一种基于动态规划的算法,利用此算法能够求得无负环有向图中任意两点间的最短路径。
无负环图中的点记为 $\lbrace{1, 2,...,n}\rbrace$,$dist(i,j,k)$ 表示 $i$ 到 $j$ 最短路径长度,路径通过的点只能是 $\lbrace{1,2,...,k}\rbrace$ 集合中的。
如何从 k-1个点扩展到 k 个点?
k 个点中,从 i 到 j 的最短路径只有两种可能,通过 k 点或不通过。若是通过 k 点,则以 k 为中间点,$dist(i,j,k)$ 能够表示为 $dist(i,k,k-1)$ 和 $dist(k,j,j-1)$ 的和值,以下图所示。
那么 dist(i,j,k)的值就为两条路径中值最小的一个。
$$min\lbrace{dist(i,k,k-1)+dist(k,j,k-1), dist(i,j,k-1)}\rbrace$$
算法伪代码表示以下,算法时间复杂度$O(|V|^3)$
for i=1 to n: for j=1 to n: dist(i,j,0) = INF for all (i,j) in E: dist(i,j,0) = l(i,j) for k=1 to n: for i=1 to n: for j=1 to n: dist(i,j,k) = min{dist(i,k,k-1)+dist(k,j,k-1), dist(i,j,k-1)}