不一樣性質的圖中,所採起的策略有所不一樣,天然存在各樣的求最短路徑的算法。算法
對於無向無權圖(也能夠假設權值爲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)}