Floyd算法(弗洛伊德算法)

Floyd算法

弗洛伊德算法一般指本詞條

Floyd算法又稱為插點法,是一種利用動態規劃的思想尋找給定的加權圖中多源點之間最短路徑的算法,與Dijkstra算法類似。該算法名稱以創始人之一、1978年圖靈獎獲得者、史丹福大學計算機科學系教授羅伯特·弗洛伊德命名。

基本介紹

  • 中文名:弗洛伊德算法
  • 外文名:Floyd(Floyd-Warshall)
  • 別名:Roy-Warshall算法
  • 時間複雜度:O(n^3)
  • 空間複雜度:O(n^2)
  • 作用:求多源最短路徑,求傳遞閉包
簡介,核心思路,路徑矩陣,狀態轉移方程,算法過程,時間複雜度與空間複雜度,優缺點分析,算法描述,參考代碼,C語言,C++語言,Matlab原始碼,pascal語言,java語言,

簡介

在計算機科學中,Floyd-Warshall算法是一種在具有正或負邊緣權重(但沒有負周期)的加權圖中找到最短路徑的算法。算法的單個執行將找到所有頂點對之間的最短路徑的長度(加權)。 雖然它不返迴路徑本身的細節,但是可以通過對算法的簡單修改來重建路徑。 該算法的版本也可用於查找關係R的傳遞閉包,或(與Schulze投票系統相關)在加權圖中所有頂點對之間的最寬路徑。
Floyd-Warshall算法是動態規劃的一個例子,並在1962年由Robert Floyd以其當前公認的形式出版。然而,它基本上與Bernard Roy在1959年先前發表的算法和1962年的Stephen Warshall中找到圖形的傳遞閉包基本相同,並且與Kleene的算法密切相關 在1956年)用於將確定性有限自動機轉換為正則表達式。算法作為三個嵌套for循環的現代公式首先由Peter Ingerman在1962年描述。
該算法也稱為Floyd算法,Roy-Warshall算法,Roy-Floyd算法或WFI算法。

核心思路

路徑矩陣

通過一個圖的權值矩陣求出它的每兩點間的最短路徑矩陣。
從圖的帶權鄰接矩陣A=[a(i,j)] n×n開始,疊代地進行n次更新,即由矩陣D(0)=A,按一個公式,構造出矩陣D(1);又用同樣地公式由D(1)構造出D(2);……;最後又用同樣的公式由D(n-1)構造出矩陣D(n)。矩陣D(n)的i行j列元素便是i號頂點到j號頂點的最短路徑長度,稱D(n)為圖的距離矩陣,同時還可引入一個後繼節點矩陣path來記錄兩點間的最短路徑。
採用鬆弛技術(鬆弛操作),對在i和j之間的所有其他點進行一次鬆弛。所以時間複雜度為O(n^3);

狀態轉移方程

狀態轉移方程如下: map[i,j]:=min{map[i,k]+map[k,j],map[i,j]};
map[i,j]表示i到j的最短距離,K是窮舉i,j的斷點,map[n,n]初值應該為0,或者按照題目意思來做。
當然,如果這條路沒有通的話,還必須特殊處理,比如沒有map[i,k]這條路。

算法過程

1,從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。
2,對於每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比已知的路徑更短。如果是更新它。
把圖用鄰接矩陣G表示出來,如果從Vi到Vj有路可達,則G[i][j]=d,d表示該路的長度;否則G[i][j]=無窮大。定義一個矩陣D用來記錄所插入點的信息,D[i][j]表示從Vi到Vj需要經過的點,初始化D[i][j]=j。把各個頂點插入圖中,比較插點後的距離與原來的距離,G[i][j] = min( G[i][j], G[i][k]+G[k][j] ),如果G[i][j]的值變小,則D[i][j]=k。在G中包含有兩點之間最短道路的信息,而在D中則包含了最短通路徑的信息。
比如,要尋找從V5到V1的路徑。根據D,假如D(5,1)=3則說明從V5到V1經過V3,路徑為{V5,V3,V1},如果D(5,3)=3,說明V5與V3直接相連,如果D(3,1)=1,說明V3與V1直接相連。

時間複雜度與空間複雜度

時間複雜度:O(n^3);

優缺點分析

Floyd算法適用於APSP(All Pairs Shortest Paths,多源最短路徑),是一種動態規划算法,稠密圖效果最佳,邊權可正可負。此算法簡單有效,由於三重循環結構緊湊,對於稠密圖,效率要高於執行|V|次Dijkstra算法,也要高於執行|V|次SPFA算法
優點:容易理解,可以算出任意兩個節點之間的最短距離,代碼編寫簡單。
缺點:時間複雜度比較高,不適合計算大量數據。

算法描述

a) 初始化:D[u,v]=A[u,v]
b) For k:=1 to n
For i:=1 to n
For j:=1 to n
If D[i,j]>D[i,k]+D[k,j] Then
D[i,j]:=D[i,k]+D[k,j];
c) 算法結束:D即為所有點對的最短路徑矩陣

參考代碼

C語言

#include<stdio.h>
#include<stdlib.h>
#define max 1000000000
int d[1000][1000],path[1000][1000];
int main()
{
    int i,j,k,m,n;
    int x,y,z;
    scanf("%d%d",&n,&m);
    
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            d[i][j]=max;
            path[i][j]=j;
    }
    
    for(i=1;i<=m;i++) {
            scanf("%d%d%d",&x,&y,&z);
            d[x][y]=z;
            d[y][x]=z;
    }
    
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++) {
                if(d[i][k]+d[k][j]<d[i][j]) {
                    d[i][j]=d[i][k]+d[k][j];
                    path[i][j]=path[i][k];
                }
            }
    for(i=1;i<=n;i++)
        for(j=1;j<=i;j++)
          if (i!=j) printf("%d->%d:%d\n",i,j,d[i][j]);
    int f, en;
    scanf("%d%d",&f,&en);
    while (f!=en) {
        printf("%d->",f);
        f=path[f][en];
    }
    printf("%d\n",en);
    return 0;
}

C++語言

#include<iostream>
#include<vector>
using namespace std;
const int &INF=100000000;
void floyd(vector<vector<int> > &distmap,//可被更新的鄰接矩陣,更新後不能確定原有邊
           vector<vector<int> > &path)//路徑上到達該點的中轉點
//福利:這個函式沒有用除INF外的任何全局量,可以直接複製!
{
    const int &NODE=distmap.size();//用鄰接矩陣的大小傳遞頂點個數,減少參數傳遞
    path.assign(NODE,vector<int>(NODE,-1));//初始化路徑數組 
    for(int k=1; k!=NODE; ++k)//對於每一個中轉點
        for(int i=0; i!=NODE; ++i)//枚舉源點
            for(int j=0; j!=NODE; ++j)//枚舉終點
                if(distmap[i][j]>distmap[i][k]+distmap[k][j])//不滿足三角不等式
                {
                    distmap[i][j]=distmap[i][k]+distmap[k][j];//更新
                    path[i][j]=k;//記錄路徑
                }
}
void print(const int &beg,const int &end,
           const vector<vector<int> > &path)//傳引用,避免拷貝,不占用記憶體空間
           //也可以用棧結構先進後出的特性來代替函式遞歸 
{
    if(path[beg][end]>=0)
    {
        print(beg,path[beg][end],path);
        print(path[beg][end],end,path);
    }
    else cout<<"->"<<end;
}
int main()
{
    int n_num,e_num,beg,end;//含義見下
    cout<<"(不處理負權迴路)輸入點數、邊數:";
    cin>>n_num>>e_num;
    vector<vector<int> > path,
          distmap(n_num,vector<int>(n_num,INF));//默認初始化鄰接矩陣
    for(int i=0,p,q; i!=e_num; ++i)
    {
        cout<<"輸入第"<<i+1<<"條邊的起點、終點、長度(100000000代表無窮大,不聯通):";
        cin>>p>>q;
        cin>>distmap[p][q];
    }
    floyd(distmap,path);
    cout<<"計算完畢,可以開始查詢,請輸入出發點和終點:";
    cin>>beg>>end;
    cout<<"最短距離為"<<distmap[beg][end]<<",列印路徑:"<<beg;
    print(beg,end,path);
}

Matlab原始碼

function Floyd(w,router_direction,MAX)
%w為此圖的距離矩陣
%router_direction為路由類型:0為前向路由;非0為回溯路由
%MAX是數據輸入時的∞的實際值
len=length(w);
flag=zeros(1,len);
%根據路由類型初始化路由表
R=zeros(len,len);
for i=1:len
if router_direction==0%前向路由
R(:,i)=ones(len,1)*i;
else %回溯路由
R(i,:)=ones(len,1)*i;
end
R(i,i)=0;
end
disp('');
disp('w(0)');
dispit(w,0);
disp('R(0)');
dispit(R,1);
%處理端點有權的問題
for i=1:len
tmp=w(i,i)/2;
if tmp~=0
w(i,:)=w(i,:)+tmp;
w(:,i)=w(:,i)+tmp;
flag(i)=1;
w(i,i)=0;
end
end
%Floyd算法具體實現過程
for i=1:len
for j=1:len
if j==i || w(j,i)==MAX
continue;
end
for k=1:len
if k==i || w(j,i)==MAX
continue;
end
if w(j,i)+w(i,k)<w(j,k) %Floyd算法核心代碼
w(j,k)=w(j,i)+w(i,k);
if router_direction==0%前向路由
R(j,k)=R(j,i);
else %回溯路由
R(j,k)=R(i,k);
end
end
end
end
%顯示每次的計算結果
disp(['w(',num2str(i),')'])
dispit(w,0);
disp(['R(',num2str(i),')'])
dispit(R,1);
end
%中心和中點的確定
[Center,index]=min(max(w'));
disp(['中心是V',num2str(index)]);
[Middle,index]=min(sum(w'));
disp(['中點是V',num2str(index)]);
end
function dispit(x,flag)
%x:需要顯示的矩陣
%flag:為0時表示顯示w矩陣,非0時表示顯示R矩陣
len=length(x);
s=[];
for j=1:len
if flag==0
s=[s sprintf('%5.2f\t',x(j,:))];
else
s=[s sprintf('%d\t',x(j,:))];
end
s=[s sprintf('\n')];
end
disp(s);
disp('---------------------------------------------------');
end
% 選擇後按Ctrl+t取消注釋號%
%
% 示例:
% a=[
% 0,100,100,1.2,9.2,100,0.5;
% 100,0,100,5,100,3.1,2;
% 100,100,0,100,100,4,1.5;
% 1.2,5,100,0,6.7,100,100;
% 9.2,100,100,6.7,0,15.6,100;
% 100,3.1,4,100,15.6,0,100;
% 0.5,2,1.5,100,100,100,0
% ];
%
% b=[
% 0,9.2,1.1,3.5,100,100;
% 1.3,0,4.7,100,7.2,100;
% 2.5,100,0,100,1.8,100;
% 100,100,5.3,0,2.4,7.5;
% 100,6.4,2.2,8.9,0,5.1;
% 7.7,100,2.7,100,2.1,0
% ];
%
% Floyd(a,1,100)
% Floyd(b,1,100)

pascal語言

program floyd;
varst,en,f:integer;
k,n,i,j,x:integer;
a:array[1..10,1..10] of integer;
path:array[1..10,1..10] of integer;
begin
readln(n);
for i:=1 to n do
begin
for j:=1 to n do
begin
read(k);
if k<>0 thena[i,j]:=k
elsea[i,j]:=maxint;
path[i,j]:=j;
end;
readln;
end;
for x:=1 to n do
for i:=1 to n do
for j:=1 to n do
if a[i,j]>a[i,x]+a[x,j] then
begin
a[i,j]:=a[i,x]+a[x,j];
path[i,j]:=path[i,x];
end;
readln(st,en);
writeln(a[st,en]);
f:=st;
while f<> en do
begin
write(f);
write('-->');
f:=path[f,en];
end;
writeln(en);
end.

java語言

//以無向圖G為入口,得出任意兩點之間的路徑長度length[i][j],路徑path[i][j][k],
//途中無連線得點距離用0表示,點自身也用0表示
public class FLOYD {
int[][] length = null;// 任意兩點之間路徑長度
int[][][] path = null;// 任意兩點之間的路徑
public FLOYD(int[][] G) {
int MAX = 100;int row = G.length;// 圖G的行數
int[][] spot = new int[row][row];// 定義任意兩點之間經過的點
int[] onePath = new int[row];// 記錄一條路徑
length = new int[row][row];
path = new int[row][row][];
for (int i = 0; i < row; i++)// 處理圖兩點之間的路徑
for (int j = 0; j < row; j++) {
if (G[i][j] == 0)G[i][j] = MAX;// 沒有路徑的兩個點之間的路徑為默認最大
if (i == j)G[i][j] = 0;// 本身的路徑長度為0
}
for (int i = 0; i < row; i++)// 初始化為任意兩點之間沒有路徑
for (int j = 0; j < row; j++)
spot[i][j] = -1;
for (int i = 0; i < row; i++)// 假設任意兩點之間的沒有路徑
onePath[i] = -1;
for (int v = 0; v < row; ++v)
for (int w = 0; w < row; ++w)
length[v][w] = G[v][w];
for (int u = 0; u < row; ++u)
for (int v = 0; v < row; ++v)
for (int w = 0; w < row; ++w)
if (length[v][w] > length[v][u] + length[u][w]) {
length[v][w] = length[v][u] + length[u][w];// 如果存在更短路徑則取更短路徑
spot[v][w] = u;// 把經過的點加入
}
for (int i = 0; i < row; i++) {// 求出所有的路徑
int[] point = new int[1];
for (int j = 0; j < row; j++) {
point[0] = 0;
onePath[point[0]++] = i;
outputPath(spot, i, j, onePath, point);
path[i][j] = new int[point[0]];
for (int s = 0; s < point[0]; s++)
path[i][j][s] = onePath[s];
}
}
}
void outputPath(int[][] spot, int i, int j, int[] onePath, int[] point) {// 輸出i// 到j// 的路徑的實際代碼,point[]記錄一條路徑的長度
if (i == j)return;
if (spot[i][j] == -1)
onePath[point[0]++] = j;
// System.out.print(" "+j+" ");
else {
outputPath(spot, i, spot[i][j], onePath, point);
outputPath(spot, spot[i][j], j, onePath, point);
}
}
public static void main(String[] args) {
int data[][] = {
{ 0, 27, 44, 17, 11, 27, 42, 0, 0, 0, 20, 25, 21, 21, 18, 27, 0 },// x1
{ 27, 0, 31, 27, 49, 0, 0, 0, 0, 0, 0, 0, 52, 21, 41, 0, 0 },// 1
{ 44, 31, 0, 19, 0, 27, 32, 0, 0, 0, 47, 0, 0, 0, 32, 0, 0 },// 2
{ 17, 27, 19, 0, 14, 0, 0, 0, 0, 0, 30, 0, 0, 0, 31, 0, 0 },// 3
{ 11, 49, 0, 14, 0, 13, 20, 0, 0, 28, 15, 0, 0, 0, 15, 25, 30 },// 4
{ 27, 0, 27, 0, 13, 0, 9, 21, 0, 26, 26, 0, 0, 0, 28, 29, 0 },// 5
{ 42, 0, 32, 0, 20, 9, 0, 13, 0, 32, 0, 0, 0, 0, 0, 33, 0 },// 6
{ 0, 0, 0, 0, 0, 21, 13, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0 },// 7
{ 0, 0, 0, 0, 0, 0, 0, 19, 0, 11, 20, 0, 0, 0, 0, 33, 21 },// 8
{ 0, 0, 0, 0, 28, 26, 32, 0, 11, 0, 10, 20, 0, 0, 29, 14, 13 },// 9
{ 20, 0, 47, 30, 15, 26, 0, 0, 20, 10, 0, 18, 0, 0, 14, 9, 20 },// 10
{ 25, 0, 0, 0, 0, 0, 0, 0, 0, 20, 18, 0, 23, 0, 0, 14, 0 },// 11
{ 21, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 27, 22, 0, 0 },// 12
{ 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0 },// 13
{ 18, 41, 32, 31, 15, 28, 0, 0, 0, 29, 14, 0, 22, 0, 0, 11, 0 },// 14
{ 27, 0, 0, 0, 25, 29, 33, 0, 33, 14, 9, 14, 0, 0, 11, 0, 9 },// 15
{ 0, 0, 0, 0, 30, 0, 0, 0, 21, 13, 20, 0, 0, 0, 0, 9, 0 } // 16
};
for (int i = 0; i < data.length; i++)
for (int j = i; j < data.length; j++)
if (data[i][j] != data[j][i])return;
FLOYD test=new FLOYD(data);
for (int i = 0; i < data.length; i++)
for (int j = i; j < data[i].length; j++) {
System.out.println();
System.out.print("From " + i + " to " + j + " path is: ");
for (int k = 0; k < test.path[i][j].length; k++)
System.out.print(test.path[i][j][k] + " ");
System.out.println();
System.out.println("From " + i + " to " + j + " length :"+ test.length[i][j]);
}
}
}

相關詞條

熱門詞條

聯絡我們