Floyd-Warshall算法(Floyd-Warshall algorithm),是一種利用動態規劃的思想尋找給定的加權圖中多源點之間最短路徑的算法,與Dijkstra算法相似。該算法名稱以創始人之1、1978年圖靈獎得到者、斯坦福大學計算機科學系教授羅伯特·弗洛伊德命名。
複製代碼
簡單的說就是解決任意兩點間的最短路徑的一種算法,能夠正確處理有向圖或負權的最短路徑問題,同時也被用於計算有向圖的傳遞閉包。Floyd-Warshall算法的時間複雜度爲O(N3),空間複雜度爲O(N2)。html
1.dijkstra算法,最經典的單源最短路徑算法 上篇文章已經講到java
2.bellman-ford算法,容許負權邊的單源最短路徑算法算法
3.spfa,實際上是bellman-ford+隊列優化,其實和bfs的關係更密一點數組
4.floyd算法,經典的多源最短路徑算法bash
今天先說說Floyd閉包
a)如圖:存在【0,1,2,3】 4個點,兩點之間的距離就是邊上的數字,若是兩點之間,沒有邊相連,則沒法到達,爲無窮大。 b)要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),並經過這個頂點k中轉即a->k->b,纔可能縮短原來從頂點a點到頂點b的路程。那麼這個中轉的頂點k是0~n中的哪一個點呢? 優化
1)如圖 0->1距離爲5,0->2不可達,距離爲∞,0->3距離爲7……依次可將圖轉化爲鄰接矩陣(主對角線,也就是自身到自身,咱們規定距離爲0,不可達爲無窮大),如圖矩陣 用於存聽任意一對頂點之間的最短路徑權值。 spa
2)再建立一個二維數組Path路徑數組,用於存聽任意一對頂點之間的最短路徑。每一個單元格的內容表示從i點到j點途經的頂點。(初始還未開始查找,默認-1)即爲: 0 -> 1 , 0 -> 2 , 0 -> 3 ,
1 -> 0 , 1 -> 2 , 1 -> 3 , 2 -> 0 , 1 -> 1 , 1 -> 3 轉化成二元數組即爲: {0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2}.net
{0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2} 從上面中二元組集合的第一個元素開始,循環執行如下過程:3d
1. 用i,j兩個變量分別指向二元組裏的兩個元素,好比{0,1}這個二元組,i指向0;j指向1
2. 判斷 (A[ i ][ 0 ]+A[ 0 ][ j ] ) < A[ i ][ j ] (即判斷 i -> j,i點到j點的距離是否小於從0點中轉的距離),若是false,則判斷下一組二元數組。
3. 若是表達式爲真,更新A[ i ] [ j ]的值爲A[ i ] [ 0 ] + A[ 0 ] [ j ],Path[ i ] [ j ]的值爲點0(即設置i到j要通過0點中轉)
複製代碼
{0,1}按照此過程執行以後,
0->0 + 0->1的距離不小於0->1 , 下一組{0,2},{0,3}, {1,0},{2,0},{3,0}也同理。{1,2}按照此過程執行,A[1,0] 無窮大, A[0,2]也是無窮大,而A[1,4] = 4,則1點到2點確定不會從0點中轉。
A[1][0]無窮大同理下一組{1,2}, {1,3}也同理。
{2,1}按照此過程執行,A[2][0] = 3 ,A[0][1]=5 ,A[2][1] = 3那麼 A[2][0]+ ,A[0][1] > A[2][1] ………… 依次類推,遍歷二元組集合,沒有0點適合作中轉的
依次類推,遍歷二元組集合{0,1},{0,2},{0,3},{1,0},{1,2},{1,3},{2,0},{2,1},{2,3},{3,0},{3,1},{3,2} ,當遍歷{3,0}時,A[3][2] = 1 ,A[2][0]=3 ,A[3][0] = 不可達,那麼 2點適合作從3點到0點之間的中轉點。 設置距離矩陣A[3][0] = 1+3 =4 ,Path矩陣Path[3][0] = 2點,表示從3到0在2點中轉,距離最近。
如圖表示(紅色單元格),從3到0,最近距離爲4,在2點中轉 。依次類推,遍歷完二元組集合
依次類推,遍歷二元組集合,直到全部的頂點都作過一次中間點爲止。
好比1點到2點怎麼走?根據路徑Path矩陣,Path[1][2] = 3,表示從點3中轉,即 1-> 3 ->2
有時候不僅經過一個點,而是通過兩個點或者更多點中轉會更短,即a->k1->k2b->或者a->k1->k2…->k->i…->b。 好比頂點1到頂點0,咱們看數組Path Path[1][0] = 3,說明頂點3是中轉點,那麼再從3到0 Path[3][0] = 2,說明從3到0,頂點2是中轉點,而後在從2到0 Path[2][0] = -1,說明頂點2到頂點0沒有途徑頂點,也就是說,能夠由頂點2直接到頂點0,即它們有邊鏈接。
最終,最短路徑爲1->3->2->0,距離爲 A[1][0] = 6 。 顯然,這是一個逐層遞進,遞歸的過程。
// 表示無窮大 即不可達
public static int MAX = Integer.MAX_VALUE;
// 距離矩陣
public int[][] dist;
// 路徑Path矩陣
public int[][] path;
複製代碼
// 核心算法
for(int k = 0 ; k < size ; k++){
for(int i = 0;i < size;i++){
for(int j = 0 ;j < size;j++){
// 判斷若是 ik距離可達且 kj距離可達 且 i和j的距離是否大於 i-> k 與 k->j的距離和
if( dist[i][k] != MAX && dist[k][j] != MAX && dist[i][j] > (dist[i][k] + dist[k][j]) ){
path[i][j]= k;
dist[i][j]= dist[i][k] + dist[k][j];
}
}
}
}
複製代碼
看完這篇文章若是你還不會Floyd,請留言評論。