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

为了能讲明白弗洛伊德(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算法代码简洁,虽简洁但也不一定好懂,还是需要多加揣摩才能领会。另外,虽然我们使用的例子都是无向图的,但它们对于有向图依然有效,只不过在创建图的时候,有向图的邻接矩阵不是对称的而已。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

P1220 关路灯

题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少)。老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上...

2826
来自专栏菩提树下的杨过

“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (下)

在前一部分的最后,我们给出了一个寻路的示例,在大多数情况下,运行还算良好,但是有一个小问题,如下图: ? 很明显,障碍物已经把路堵死了,但是小球仍然穿过对角线跑...

1859
来自专栏数据结构与算法

BZOJ3262: 陌上花开(cdq分治)

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。

932
来自专栏聊聊技术

原 初学图论-Kahn拓扑排序算法(Kah

2738
来自专栏算法修养

天梯赛 登顶题解

L 3-005 肿瘤诊断 题目链接: https://www.patest.cn/contests/gplt/L3-004 三维求连通块: 用并查集,或者广...

4037
来自专栏小樱的经验随笔

元胞自动机实现多数分类算法

元胞自动机(Cellular automaton) 元胞自动机是由元胞组成的网格,每个元胞都根据邻域的状态来选择开或关。所有的元胞都遵循同样的规则,也称为元胞的...

2855
来自专栏大学生计算机视觉学习DeepLearning

c++ MFC图像处理CImage类常用操作代码

原文地址:https://www.cnblogs.com/DOMLX/p/9598974.html

663
来自专栏xingoo, 一个梦想做发明家的程序员

AOE关键路径

这个算法来求关键路径,其实就是利用拓扑排序,首先求出,每个节点最晚开始时间,再倒退求每个最早开始的时间。 从而算出活动最早开始的时间和最晚开始的时间,如果这两个...

1947
来自专栏开发 & 算法杂谈

凸包问题之GrahamScan解法

当沿着Convex hull逆时针漫游时,总是向左转在极坐标系下按照极角大小排列,然后逆时针方向漫游点集,去除非Convex hull顶点(非左 转点)。

714
来自专栏用户2442861的专栏

Python-OpenCV 处理图像(二):滤镜和图像运算

喜欢自拍的人肯定都知道滤镜了,下面代码尝试使用一些简单的滤镜,包括图片的平滑处理、灰度化、二值化等:

471

扫码关注云+社区