Floyd算法


轉自https://blog.csdn.net/qq_35644234/article/details/60875818

1、最短路徑問題介紹

問題解釋:
從圖中的某個頂點出發到達另外一個頂點的所經過的邊的權重和最小的一條路徑,稱為最短路徑

解決問題的算法:

之前已經對Dijkstra算法做了介紹(不懂的可以看這篇博客:Dijkstra算法詳解),所以這篇博客打算對Floyd算法做詳細的的介紹。

2、Floyd算法的介紹(動態規划)

  通過這種方法我們可以求出任意兩個點之間最短路徑。它的時間復雜度是O(N3)。令人很震撼的是它竟然只有五行代碼,實現起來非常容易。正是因為它實現起來非常容易,如果時間復雜度要求不高,使用Floyd-Warshall來求指定兩點之間的最短路或者指定一個點到其余各個頂點的最短路徑也是可行的。當然也有更快的算法,請看下一節:Dijkstra算法。

       另外需要注意的是:Floyd-Warshall算法不能解決帶有“負權回路”(或者叫“負權環”)的圖,因為帶有“負權回路”的圖沒有最短路。例如下面這個圖就不存在1號頂點到3號頂點的最短路徑。因為1->2->3->1->2->3->…->1->2->3這樣路徑中,每繞一次1->-2>3這樣的環,最短路就會減少1,永遠找不到最短路。其實如果一個圖中帶有“負權回路”那么這個圖則沒有最短路。
081030elthvel6et6k886y.png
  • 算法的特點:
    弗洛伊德算法是解決任意兩點間的最短路徑的一種算法,可以正確處理有向圖或無向圖或負權(但不可存在負權回路)的最短路徑問題,同時也被用於計算有向圖的傳遞閉包。

  • 算法的思路

  通過Floyd計算圖G=(V,E)中各個頂點的最短路徑時,需要引入兩個矩陣,矩陣D中的元素d[i][j]表示頂點i(第i個頂點)到頂點j(第j個頂點)的距離。矩陣P中的元素p[i][j],表示頂點i到頂點j經過了p[i][j]記錄的值所  表示的頂點。

  假設圖G中頂點個數為N,則需要對矩陣D和矩陣P進行N次更新。初始時,矩陣D中頂點d[i][j]的距離為頂點i到頂點j的權值;如果i和j不相鄰,則d[i][j]=∞,矩陣P的值為頂點p[i][j]的j的值。 接下來開始,對矩  陣D進行N次更新。第1次更新時,如果”d[i][j]的距離” > “d[i][0]+d[0][j]”(d[i][0]+d[0][j]表示”i與j之間經過第1個頂點的距離”),則更新d[i][j]為”d[i][0]+d[0][j]”,更新p[i][j]=p[i][0]。 同理,第k次更新時,如果”d[i][j]的  距離” > “d[i][k-1]+d[k-1][j]”,則更新d[i][j]為”d[i][k-1]+d[k-1][j]”,p[i][j]=p[i][k-1]。更新N次之后,操作完成!

 

3、Floyd算法的實例過程

上面,我們已經介紹了算法的思路,如果,你覺得還是不理解,那么通過一個實際的例子,把算法的過程過一遍,你就明白了,如下圖,我們求下圖的每個點對之間的最短路徑的過程如下:

這里寫圖片描述

第一步,我們先初始化兩個矩陣,得到下圖兩個矩陣: D矩陣是“鄰接矩陣”
這里寫圖片描述

這里寫圖片描述

第二步,以v1為中階,更新兩個矩陣:
發現,a[1][0]+a[0][6] < a[1][6] 和a[6][0]+a[0][1] < a[6][1],所以我們只需要矩陣D和矩陣P,結果如下:

這里寫圖片描述

這里寫圖片描述

通過矩陣P,我發現v2–v7的最短路徑是:v2–v1–v7

第三步:以v2作為中介,來更新我們的兩個矩陣,使用同樣的原理,掃描整個矩陣,得到如下圖的結果:

這里寫圖片描述
這里寫圖片描述

OK,到這里我們也就應該明白Floyd算法是如何工作的了,他每次都會選擇一個中介點,然后,遍歷整個矩陣,查找需要更新的值,下面還剩下五步,就不繼續演示下去了,理解了方法,我們就可以寫代碼了。

public class Floyd {
private int[][] matrix;//初始的鄰接矩陣(未更新前的矩陣)
private char[] nodes;//節點集合

static int INF=Integer.MAX_VALUE;

public static void main(String[] args){
int[][] matrix={{0,5,INF},{INF,0,6},{7,9,0}};
char[] nodes={'a','b','c'};
Floyd floyd=new Floyd(matrix,nodes);

//長度數組。即,dist[i][j]=sum表示,"頂點i"到"頂點j"的最短路徑的長度是sum。
int[][] distance=new int[nodes.length][nodes.length];
//路徑。path[i][j]=k表示,"頂點i"到"頂點j"的最短路徑會經過頂點k。
int[][] paths=new int[nodes.length][nodes.length];

floyd.floydFunction(distance,paths);
}

public Floyd(int[][] matrix,char[] nodes){
this.matrix=matrix;
this.nodes=nodes;
}

public void floydFunction(int[][] distance,int[][] paths){
//初始化distance[][],paths[][]
for (int i=0;i<nodes.length;i++){
for (int j=0;j<nodes.length;j++){
distance[i][j]=matrix[i][j];//"頂點i"到"頂點j"的路徑長度為"i到j的權值"。
paths[i][j]=j;//"頂點i"到"頂點j"的最短路徑是經過頂點j。
}
}
System.out.printf("Floyd算法執行前 distance: \n");
print(distance);
System.out.printf("Floyd算法執行前 paths: \n");
print(paths);

//循環更新distance[][],paths[][]
for (int k=0;k<nodes.length;k++){
for (int i=0;i<nodes.length;i++){
for (int j=0;j<nodes.length;j++){
int temp=(distance[i][k]==INF||distance[k][j]==INF)?INF:(distance[i][k]+distance[k][j]);
if(distance[i][j]>temp){
//// "i到j最短路徑"對應的值設,為更小的一個(即經過k)
distance[i][j]=Math.min(temp,distance[i][j]);
// "i到j最短路徑"對應的路徑,經過k
paths[i][j]=k;
}
}
}
}
System.out.printf("Floyd算法執行后 distance: \n");
print(distance);
System.out.printf("Floyd算法執行后 paths: \n");
print(paths);
}
// 打印floyd最短路徑的結果
public void print(int[][] distance){
for (int i = 0; i < distance.length; i++) {
for (int j = 0; j < distance.length; j++)
System.out.printf("%2d ", distance[i][j]);
System.out.printf("\n");
}
}
}

執行的結果是:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM