定義:設G是一個有向圖,其中每條邊(i, j)都有一個非負的長度L[i, j],若點i 到點j 沒有邊相連,則設L[i, j] = ∞. 要找出每個頂點到其他所有頂點的最短路徑所對應的長度。
如:
則,L: 0 2 9
8 0 6
1 ∞ 0
運用Floyd-Warshall算法, 時間復雜度為O(n3),空間復雜度為O(n2).
算法基本思路:
引理,點 i 到點 j 的最短路徑可能是點 i 到點 j 的直接路徑長度,也可能是以某點 k 為中間節點,i, k, j 的路徑長度。
采用自底向上逐步求解的方法,設D[i, j, k] 表示 點 i 到 j 以點集[0..k] 為中間節點的最短路徑。
k 從0開始枚舉各點,則第一步先求所有點以可能經過點0為中間節點的最短路徑;
第二步求所有點以可能經過點1為中間節點的最短路徑,在此時所有的D[i, j] 已經考慮過0作為中間節點的最短路徑,即第二步做的是以點集[0..1]作為考慮,滿足自底向上,不難看出,到了最后一步,就是以點集[0...n-1]作為考慮,此時求得的D[i, j] 就是最短路徑。
算法具體實現:
輸入:L[0...n-1][0...n-1] L[i][j] 表示 i 到 j 的直接路徑長度
為了節約空間,可在原來的空間上做自底向上求解,即D只需二維。
首先初始化D[0...n-1][0...n-1],令 D[i][j] = L[i][j],無窮大的數就初始化為一個當前類型的最大值。
然后從0開始迭代k,D[i][j] = min(D[i][j], D[i][k] + D[k][j]),注意可能出現的無窮大值,即i 到 k 沒有直接路徑或k 到 j 沒有直接路徑,此時需要做比較,否則相加可能會出現“奇怪”的數字。
代碼:

/* input: l[0..n-1][0..n-1] //l[i][j] = the stright length between point i and j table: d[0..n-1][0..n-1] //d[i][j] = the minimum length between point i and j enumerate k, 0<= k <=n-1, d[i][j] = min(l[i][j], d[i][k]+d[k][j]) */ public class Floyd { public static int[][] floyd(int[][] l){ int[][] d = new int[l.length][l.length]; //init d[i][j] = l[i][j] for(int i = 0; i < d.length; i ++){ System.arraycopy(l[i], 0, d[i], 0, d.length); } //compute for(int k = 0; k < d.length; k ++){ for(int i = 0; i < d.length; i ++){ for(int j = 0; j < d.length; j ++){ if(d[i][k] != Integer.MAX_VALUE && d[k][j] != Integer.MAX_VALUE) d[i][j] = Math.min(d[i][j], d[i][k] + d[k][j]); } } } return d; } public static void main(String[] args) { int[][] l = { {0, 7, 1, 6}, {Integer.MAX_VALUE, 0, 9, Integer.MAX_VALUE}, {4, 4, 0, 2}, {1, Integer.MAX_VALUE, Integer.MAX_VALUE, 0} }; int[][] d = floyd(l); for(int i = 0; i < d.length; i ++){ for(int j = 0; j < d[i].length; j ++){ System.out.print(d[i][j] + " "); } System.out.println(); } } }