具體步驟和圖解看這個:https://www.cnblogs.com/ssyfj/p/9495960.html
一看這個就懂了-傻子也能看懂的弗洛伊德算法:https://www.cnblogs.com/wangyuliang/p/9216365.html
知識點: 1)Floyd算法適用於APSP(All Pairs Shortest Paths,多源最短路徑),是一種動態規划算法,稠密圖效果最佳。 2)時間復雜度比較高O(n*3),不適合計算大量數據。 3)可以算出任意兩個節點之間的最短距離 4)無論是迪傑斯特拉算法還是弗洛伊德算法,對於有向圖,無向圖都是可以使用的。另外我們的最短路徑一般都是針對有環圖,無環圖使用拓撲排序可以獲得
5)弗洛伊德算法是用來求所有頂點到所有頂點的時間復雜度。
6)Floyd(Floyd-Warshall)算法邊權可正可負,但是不能解決帶有“負權回路”(或者叫“負權環”)的圖,因為帶有“負權回路”的圖沒有最短路。
7)其實如果一個圖中帶有“負權回路”那么這個圖則沒有最短路。
(如果存在一個環(從某個點出發又回到自己的路徑),而且這個環上所有權值之和是負數,那這就是一個負權環,也叫負權回路)
如下圖:不存在1號頂點到3號頂點的最短路徑。因為1->2->3->1->2->3->...->1->2->3這樣路徑中,
每繞一次1->-2>3這樣的環,最短路就會減少1,永遠找不到最短路。
1:Floyd算法過程矩陣的計算----十字交叉法:
方法:兩條線,從左上角開始計算一直到右下角 如下所示
給出矩陣,其中矩陣A是鄰接矩陣,而矩陣Path記錄u,v兩點之間最短路徑所必須經過的點
相應計算方法如下:
最后A3即為所求結果
2:實現代碼
1)弗洛伊德算法定義了兩個二維矩陣: 矩陣D記錄頂點間的最小路徑 ;例如D[0][3]= 10,說明頂點0 到 3 的最短路徑為10。 矩陣P記錄頂點間最小路徑中的中轉點 ;例如P[0][3]= 1 說明,0 到 3的最短路徑軌跡為:0 -> 1 -> 3。 它通過3重循環,k為中轉點,v為起點,w為終點,循環比較D0[v][w] 和 D0[v][k] + D0[k][w] 最小值,如果D0[v][k] + D0[k][w] 為更小值, 則把D0[v][k] + D0[k][w] 覆蓋保存在D1[v][w]中。 2)核心思想:D1[v][w] = min{D0[v][k] + D0[k][w],D0[v][w]}【D0代表原來未更新前的數據,D1表示我們修改更新后的新的數據】
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> #include "queue.h" #define MAXVEX 100 //最大頂點數 #define INFINITY 65535 //用0表示∞ typedef char VertexType; //頂點類型,字符型A,B,C,D... typedef int EdgeType; //邊上權值類型10,15,... //鄰接矩陣結構 typedef struct { VertexType vers[MAXVEX]; //頂點表 EdgeType arc[MAXVEX][MAXVEX]; //鄰接矩陣,可看作邊表 int numVertexes, numEdges; //圖中當前的頂點數和邊數 }MGraph; typedef int Dist[MAXVEX][MAXVEX]; //存放各個頂點到其余頂點的最短路徑權值和 typedef int Path[MAXVEX][MAXVEX]; //存放各個頂點到其余頂點前驅頂點位置 //創建鄰接矩陣 void CreateMGraph(MGraph* G); //顯示鄰接矩陣 void showGraph(MGraph G); void Floyd(MGraph G,Path* path,Dist* dist); void ShowDistAndPath(Path P, Dist D,int n); void Floyd(MGraph G, Path* path, Dist* dist) { int i,j,k; //初始化path和dist for (i = 0; i < G.numVertexes;i++) { for (j = 0; j < G.numVertexes;j++) { (*dist)[i][j] = G.arc[i][j]; (*path)[i][j] = j; //初始化為這個的一個好處就是自己到自己的路徑就是自己,我們不用修改 } } //使用弗洛伊德核心算法,三層循環求解 for (k = 0; k < G.numVertexes;k++) { for (i = 0; i < G.numVertexes;i++) { for (j = 0; j < G.numVertexes;j++) { if ((*dist)[i][j]>((*dist)[i][k]+(*dist)[k][j])&&i!=j) { //將權值和更新,路徑也變為中轉點 (*dist)[i][j] = (*dist)[i][k] + (*dist)[k][j]; (*path)[i][j] = (*path)[i][k]; } } } } } void ShowDistAndPath(Path P, Dist D,int n) { int i, j; printf("Printf Dist:\n"); for (i = 0; i < n;i++) { for (j = 0; j < n; j++) { if (i==j) printf(" 0"); //需要將我們的無窮轉換一下再顯示 else printf("%5d", D[i][j]); } printf("\n"); } printf("Printf Path:\n"); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) printf("%5d", P[i][j]); printf("\n"); } } int main() { MGraph MG; CreateMGraph(&MG); showGraph(MG); Path path; Dist dist; Floyd(MG, &path, &dist); ShowDistAndPath(path, dist, MG.numVertexes); system("pause"); return 0; } //生成鄰接矩陣 void CreateMGraph(MGraph* G) { int i, j, k, w; G->numVertexes = 9; G->numEdges = 16; //讀入頂點信息 G->vers[0] = 'A'; G->vers[1] = 'B'; G->vers[2] = 'C'; G->vers[3] = 'D'; G->vers[4] = 'E'; G->vers[5] = 'F'; G->vers[6] = 'G'; G->vers[7] = 'H'; G->vers[8] = 'I'; //getchar(); //可以獲取回車符 for (i = 0; i < G->numVertexes; i++) for (j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; //鄰接矩陣初始化 //創建了有向鄰接矩陣 G->arc[0][1] = 1; G->arc[0][2] = 5; G->arc[1][2] = 3; G->arc[1][3] = 7; G->arc[1][4] = 5; G->arc[2][4] = 1; G->arc[2][5] = 7; G->arc[3][4] = 2; G->arc[3][6] = 3; G->arc[4][5] = 3; G->arc[4][6] = 6; G->arc[4][7] = 9; G->arc[5][7] = 5; G->arc[6][7] = 2; G->arc[6][8] = 7; G->arc[7][8] = 4; for (i = 0; i < G->numVertexes;i++) for (k = i; k < G->numVertexes;k++) G->arc[k][i] = G->arc[i][k]; } //顯示鄰接矩陣 void showGraph(MGraph G) { for (int i = 0; i < G.numVertexes; i++) { for (int j = 0; j < G.numVertexes; j++) { if (G.arc[i][j] != INFINITY) printf("%5d", G.arc[i][j]); else printf(" 0"); } printf("\n"); } }