算法:最短路徑之弗洛伊德(Floyd)算法


https://cloud.tencent.com/developer/article/1012420

 

為了能講明白弗洛伊德(Floyd)算法的主要思想,我們先來看最簡單的案例。圖7-7-12的左圖是一個簡單的3個頂點的連通網圖。

我們先定義兩個二維數組D[3][3]和P[3][3], D代表頂點與頂點的最短路徑權值和的矩陣。P代表對應頂點的最短路徑的前驅矩陣。在未分析任何頂點之前,我們將D命名為D(-1),其實它就是初始圖的鄰接矩陣。將P命名為P(-1), 初始化為圖中的矩陣。

首先我們來分析,所有的頂點經過v0后到達另一頂點的最短路徑。因為只有3個頂點,因此需要查看v1->v0->v2,得到

D(-1)[1][0] + D(-1)[0][2] = 3。D(-1)[1][2]表示的是v1->v2的權值為5,我們發現D(-1)[1][2] > D(-1)[1][0] + D(-1)[0][2] ,通俗話來說就是

v1->v0->v2 比v1->v2距離還要近。所以我們就讓 D(-1)[1][2] = D(-1)[1][0] + D(-1)[0][2] = 3, 同樣地D(-1)[2][1] = 3, 於是就有了D(0)矩陣。因為有變化,所以P矩陣對應的P(-1)[1][2]和P(-1)[2][1]也修改為當前中轉的頂點v0的下標0, 於是就有了P(0)。也就是說

接下來,也就是在D(0)和P(0)的基礎上繼續處理所有頂點經過v1和v2后到達另一頂點的最短路徑,得到D(1)和P(1)、D(2)和P(2)完成所有頂點到所有頂點的最短路徑計算工作。

首先我們針對圖7-7-13的左網圖准備兩個矩陣D(-1)和P(-1),D(-1)就是網圖的鄰接矩陣,P(-1)初設為P[i][j]=j 這樣的矩陣。主要用來存儲路徑。

代碼如下(改編自《大話數據結構》):注意因為是要求所有頂點到所有頂點的最短路徑,因為使用二維數組。

#include<iostream>
using namespace std;

#define MAXEDGE 20
#define MAXVEX 20
#define INFINITY 65535

typedef struct
{
    int vexs[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
} MGraph;

typedef int Patharc[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];

/* 構建圖 */
void CreateMGraph(MGraph *G)
{
    int i, j;

    /* printf("請輸入邊數和頂點數:"); */
    G->numEdges = 16;
    G->numVertexes = 9;

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */
    {
        G->vexs[i] = i;
    }

    for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            if (i == j)
                G->arc[i][j] = 0;
            else
                G->arc[i][j] = G->arc[j][i] = 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(j = i; j < G->numVertexes; j++)
        {
            G->arc[j][i] = G->arc[i][j];
        }
    }

}
/* Floyd算法,求網圖G中各頂點v到其余頂點w的最短路徑P[v][w]及帶權長度D[v][w]。 */
void ShortestPath_Floyd(MGraph MG, Patharc P, ShortPathTable D)
{
    int v, w, k;
    for (v = 0; v < MG.numVertexes; v++)/* 初始化D與P */
    {
        for (w = 0; w < MG.numVertexes; w++)
        {
            D[v][w] = MG.arc[v][w];/* D[v][w]值即為對應點間的權值 */
            P[v][w] = w;/* 初始化P */
        }
    }

    for (k = 0; k < MG.numVertexes; k++)
    {
        for (v = 0; v < MG.numVertexes; v++)
        {
            for (w = 0; w < MG.numVertexes; w++)
            {
                /* 如果經過下標為k頂點路徑比原兩點間路徑更短 */
                if (D[v][w] > D[v][k] + D[k][w])
                {
                    /* 將當前兩點間權值設為更小的一個 */
                    D[v][w] = D[v][k] + D[k][w];
                    P[v][w] = P[v][k];/* 路徑設置為經過下標為k的頂點 */
                }
            }
        }
    }
}

int main(void)
{
    int v, w, k;
    MGraph MG;
    Patharc P;
    ShortPathTable D;
    CreateMGraph(&MG);
    ShortestPath_Floyd(MG, P, D);

    cout << "各頂點間最短路徑如下: " << endl;

    for (v = 0; v < MG.numVertexes; v++)
    {
        for (w = v + 1; w < MG.numVertexes; w++)
        {
            cout << "v" << v << "--" << "v" << w << " weight: " << D[v][w]
                 << " Path: " << v << ' ';
            k = P[v][w];
            while (k != w)
            {
                cout << "-> " << k << " ";
                k = P[k][w];
            }
            cout << "-> " << w << endl;
        }
        cout << endl;
    }

    return 0;
}

輸出為:

程序中的算法代碼非常簡潔,即用了一個三層循環,k代表的是中轉結點的下標,v代表起始結點,w代表結束終點。k = 0 ~ 8,表示針對每個頂點作為中轉結點得到的計算結果,最終當k = 8時,兩矩陣數據如圖7-7-16所示。

從上圖我們可以看到第v2行的數值與Dijkstra算法求得的D數組的數值完全一樣,都是{4, 3, 0, 3, 1, 4, 6, 8, 12 }, 而且這里是所有頂點到所有頂點的最短路徑權值和都可以計算得出。那么如何由P這個路徑數組得出具體的最短路徑呢?以v2到v8為例,P[2][8] = 4,說明要經過頂點v4, 將4替換2,P[4][8] = 3, 說明經過v3, ......., 最終推導出最短路徑為:v2->v4->v3->v6->v7->v8。

Floyd算法使用了三層循環,故時間復雜度也為O(n^3),與Dijkstra算法一致,不過Floyd算法代碼簡潔,雖簡潔但也不一定好懂,還是需要多加揣摩才能領會。另外,雖然我們使用的例子都是無向圖的,但它們對於有向圖依然有效,只不過在創建圖的時候,有向圖的鄰接矩陣不是對稱的而已。


免責聲明!

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



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