圖論之最短路徑 弗洛伊德演算法(Floyd)多源最短
一:
該演算法解決的問題是對於一個圖,對該圖的沒一對頂點vi != vj,要求求出vi與vj之間的最短路徑和最短路徑長度。
二:演算法思想
正如我們所知道的,Floyd演算法用於求最短路徑。Floyd演算法可以說是Warshall演算法的擴充,三個for迴圈就可以解決問題,所以它的時間複雜度為O(n^3)。
Floyd演算法的基本思想如下:從任意節點A到任意節點B的最短路徑不外乎2種可能,1是直接從a到b,2是從a經過若干個節點x到b。所以,我們假設A[a][b]為節點a到節點b的最短路徑的距離,對於每一個節點x,我們檢查A[a][x] + A[x][b] < A[a][b]是否成立,如果成立,證明從a到x再到b的路徑比a直接到b的路徑短,我們便設定A[a][b]=A[a][x]+A[x][b],這樣一來,當我們遍曆完所有節點x,A[a][b]中記錄的便是a到b的最短路徑的距離。
接下來都是一段代碼更簡明闡述:
for(k=0;k<n;k++)
{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(k==i||k==j)
continue;
if(A[i][k]+A[k][j]<A[i][j])
{
A[i][j]=A[i][k]+A[k][j];
path[i][j]=path[k][j];
}
}
}
}
但是這裡我們要注意迴圈的嵌套順序,如果把檢查所有節點X放在最內層,那麼結果將是不正確的,為什麼呢?因為這樣便過早的把i到j的最短路徑確定下來了,而當後面存在更短的路徑時,已經不再會更新了。
因此上段代碼的迴圈嵌套才是正確的
三:演算法的實現
在實現時,需要兩個數組:
1:數組A:使用同一個數組A[i][j]開存放一系列的當前的A[i][j],初始時,A[i][j]=Edge[i][j],演算法結束時A[i][j]就是vi與vj的最短路徑長度
2:path數組:path[i][j]是從vi到vj的最短路徑上頂點vj的前一個頂點的序號,主要用於路徑的列印
四:具體代碼
1:
void Floyd()
{
int i,j,k;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
A[i][j]=Edge[i][j];//對A[][]初始化
if(i!=j&&A[i][j]<INF)
path[i][j]=i;//i到j有路徑
else
path[i][j]=-1;//i到j無路徑
}
}
//從A(-1)遞推到A(0)...A(n-1)
//或者理解成依次將v0,v1.....v(n-1)作為中間頂點
for(k=0;k<n;k++)
{
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(k==i||k==j)
continue;
if(A[i][k]+A[k][j]<A[i][j])
{
A[i][j]=A[i][k]+A[k][j];
path[i][j]=path[k][j];
}
}
}
}
}
2:主函數的情況
int main()
{
int i,j;
int u,v,w;
cin>>n;
for(i=0;i<n;i++)//處理接接矩陣
{
for(j=0;j<n;j++)
{
Edge[i][j]=INF;
}
}
for(i=0;i<n;i++)//方陣A的要求
{
Edge[i][i]=0;
}
while(1)
{
cin>>u>>v>>w;
if(u==-1&&v==-1&&w==-1)
break;
Edge[u][v]=w;
}
Floyd();
int shortest[MAXN];//從這開始就是為了列印路徑
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(i==j)
continue;
cout<<i<<"=>"<<j<<'\t'<<A[i][j];
memset(shortest,0,sizeof(shortest));
int k=0;
shortest[k]=j;
while(path[i][shortest[k]]!=i)
{
k++;
shortest[k]=path[i][shortest[k-1]];
}
k++;
shortest[k]=i;
}
}
for(int t=k;t>0;t--)
cout<<shortest[t]<<"->";
cout<<shortest[0]<<endl;
return 0;
}
//求指教