基本思想

通过Floyd计算图G=(V,E)中各个顶点的最短路径时,需要引入一个矩阵S,矩阵S中的元素a[i][j]表示顶点i(第i个顶点)到顶点j(第j个顶点)的距离。

假设图G中顶点个数为N,则需要对矩阵S进行N次更新。初始时,矩阵S中顶点a[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞。 接下来开始,对矩阵S进行N次更新。第1次更新时,如果"a[i][j]的距离" > "a[i][0]+a[0][j]"(a[i][0]+a[0][j]表示"i与j之间经过第1个顶点的距离"),则更新a[i][j]为"a[i][0]+a[0][j]"。 同理,第k次更新时,如果"a[i][j]的距离" > "a[i][k]+a[k][j]",则更新a[i][j]为"a[i][k]+a[k][j]"。更新N次之后,操作完成!

单纯的看上面的理论可能比较难以理解,下面通过实例来对该算法进行说明。

弗洛伊德算法图解

以上图G4为例,来对弗洛伊德进行算法演示。

初始状态:S是记录各个顶点间最短路径的矩阵。 
第1步:初始化S。 
    矩阵S中顶点a[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞。实际上,就是将图的原始矩阵复制到S中。 
    注:a[i][j]表示矩阵S中顶点i(第i个顶点)到顶点j(第j个顶点)的距离。

第2步:以顶点A(第1个顶点)为中介点,若a[i][j] > a[i][0]+a[0][j],则设置a[i][j]=a[i][0]+a[0][j]。 
    以顶点a[1]6,上一步操作之后,a[1][6]=∞;而将A作为中介点时,(B,A)=12,(A,G)=14,因此B和G之间的距离可以更新为26。

同理,依次将顶点B,C,D,E,F,G作为中介点,并更新a[i][j]的大小。

弗洛伊德算法的代码说明

以"邻接矩阵"为例对弗洛伊德算法进行说明,对于"邻接表"实现的图在后面会给出相应的源码。

1. 基本定义

// 邻接矩阵
typedef struct _graph
{char vexs[MAX];       // 顶点集合int vexnum;           // 顶点数int edgnum;           // 边数int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;

Graph是邻接矩阵对应的结构体。 
vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示"顶点i(即vexs[i])"和"顶点j(即vexs[j])"是邻接点;matrix[i][j]=0,则表示它们不是邻接点。

2. 弗洛伊德算法

/** floyd最短路径。* 即,统计图中各个顶点间的最短路径。** 参数说明:*        G -- 图*     path -- 路径。path[i][j]=k表示,"顶点i"到"顶点j"的最短路径会经过顶点k。*     dist -- 长度数组。即,dist[i][j]=sum表示,"顶点i"到"顶点j"的最短路径的长度是sum。*/
void floyd(Graph G, int path[][MAX], int dist[][MAX])
{int i,j,k;int tmp;// 初始化for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++){dist[i][j] = G.matrix[i][j];    // "顶点i"到"顶点j"的路径长度为"i到j的权值"。path[i][j] = j;                 // "顶点i"到"顶点j"的最短路径是经过顶点j。}}// 计算最短路径for (k = 0; k < G.vexnum; k++){for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++){// 如果经过下标为k顶点路径比原两点间路径更短,则更新dist[i][j]和path[i][j]tmp = (dist[i][k]==INF || dist[k][j]==INF) ? INF : (dist[i][k] + dist[k][j]);if (dist[i][j] > tmp){// "i到j最短路径"对应的值设,为更小的一个(即经过k)dist[i][j] = tmp;// "i到j最短路径"对应的路径,经过kpath[i][j] = path[i][k];}}}}// 打印floyd最短路径的结果printf("floyd: \n");for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++)printf("%2d  ", dist[i][j]);printf("\n");}
}

弗洛伊德算法的源码

这里分别给出"邻接矩阵图"和"邻接表图"的弗洛伊德算法源码。

1. 邻接矩阵源码(matrix_udg.c)

2. 邻接表源码(list_udg.c)

Floyd算法的实例过程

上面,我们已经介绍了算法的思路,如果,你觉得还是不理解,那么通过一个实际的例子,把算法的过程过一遍,你就明白了,如下图,我们求下图的每个点对之间的最短路径的过程如下:

第一步,我们先初始化两个矩阵,得到下图两个矩阵: 

第二步,以v1为中阶,更新两个矩阵: 
发现,a[1][0]+a[0][6] < a[1][6] 和a[6][0]+a[0][1] < a[6][1],所以我们只需要矩阵D和矩阵P,结果如下:

通过矩阵P,我发现v2–v7的最短路径是:v2–v1–v7

第三步:以v2作为中介,来更新我们的两个矩阵,使用同样的原理,扫描整个矩阵,得到如下图的结果:


OK,到这里我们也就应该明白Floyd算法是如何工作的了,他每次都会选择一个中介点,然后,遍历整个矩阵,查找需要更新的值,下面还剩下五步,就不继续演示下去了,理解了方法,我们就可以写代码了。

4、Floyd算法的代码实现

  • Floyd.h文件代码
/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成时间:2017/3/11                   */
/*                有任何问题请联系:2930526477@qq.com       */
/************************************************************/
//@尽量写出完美的程序#pragma once
//#pragma once是一个比较常用的C/C++杂注,
//只要在头文件的最开始加入这条杂注,
//就能够保证头文件只被编译一次。/*
本博客开始对Floyd算法的使用邻接矩阵实现的
*/#include<iostream>
#include<string>
using namespace std;class Graph_DG {
private:int vexnum;   //图的顶点个数int edge;     //图的边数int **arc;   //邻接矩阵int ** dis;   //记录各个顶点最短路径的信息int ** path;  //记录各个最短路径的信息
public://构造函数Graph_DG(int vexnum, int edge);//析构函数~Graph_DG();// 判断我们每次输入的的边的信息是否合法//顶点从1开始编号bool check_edge_value(int start, int end, int weight);//创建图void createGraph(int);//打印邻接矩阵void print();//求最短路径void Floyd();//打印最短路径void print_path();
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • Floyd.cpp文件代码
#include"Floyd.h"//构造函数
Graph_DG::Graph_DG(int vexnum, int edge) {//初始化顶点数和边数this->vexnum = vexnum;this->edge = edge;//为邻接矩阵开辟空间和赋初值arc = new int*[this->vexnum];dis = new int*[this->vexnum];path = new int*[this->vexnum];for (int i = 0; i < this->vexnum; i++) {arc[i] = new int[this->vexnum];dis[i] = new int[this->vexnum];path[i] = new int[this->vexnum];for (int k = 0; k < this->vexnum; k++) {//邻接矩阵初始化为无穷大arc[i][k] = INT_MAX;}}
}
//析构函数
Graph_DG::~Graph_DG() {for (int i = 0; i < this->vexnum; i++) {delete this->arc[i];delete this->dis[i];delete this->path[i];}delete dis;delete arc;delete path;
}// 判断我们每次输入的的边的信息是否合法
//顶点从1开始编号
bool Graph_DG::check_edge_value(int start, int end, int weight) {if (start<1 || end<1 || start>vexnum || end>vexnum || weight < 0) {return false;}return true;
}void Graph_DG::createGraph(int kind) {cout << "请输入每条边的起点和终点(顶点编号从1开始)以及其权重" << endl;int start;int end;int weight;int count = 0;while (count != this->edge) {cin >> start >> end >> weight;//首先判断边的信息是否合法while (!this->check_edge_value(start, end, weight)) {cout << "输入的边的信息不合法,请重新输入" << endl;cin >> start >> end >> weight;}//对邻接矩阵对应上的点赋值arc[start - 1][end - 1] = weight;//无向图添加上这行代码if(kind==2)arc[end - 1][start - 1] = weight;++count;}
}void Graph_DG::print() {cout << "图的邻接矩阵为:" << endl;int count_row = 0; //打印行的标签int count_col = 0; //打印列的标签//开始打印while (count_row != this->vexnum) {count_col = 0;while (count_col != this->vexnum) {if (arc[count_row][count_col] == INT_MAX)cout << "∞" << " ";elsecout << arc[count_row][count_col] << " ";++count_col;}cout << endl;++count_row;}
}void Graph_DG::Floyd() {int row = 0;int col = 0;for (row = 0; row < this->vexnum; row++) {for (col = 0; col < this->vexnum; col++) {//把矩阵D初始化为邻接矩阵的值this->dis[row][col] = this->arc[row][col];//矩阵P的初值则为各个边的终点顶点的下标this->path[row][col] = col;}}//三重循环,用于计算每个点对的最短路径int temp = 0;int select = 0;for (temp = 0; temp < this->vexnum; temp++) {for (row = 0; row < this->vexnum; row++) {for (col = 0; col < this->vexnum; col++) {//为了防止溢出,所以需要引入一个select值select = (dis[row][temp] == INT_MAX || dis[temp][col] == INT_MAX) ? INT_MAX : (dis[row][temp] + dis[temp][col]);if (this->dis[row][col] > select) {//更新我们的D矩阵this->dis[row][col] = select;//更新我们的P矩阵this->path[row][col] = this->path[row][temp];}}}}
}void Graph_DG::print_path() {cout << "各个顶点对的最短路径:" << endl;int row = 0;int col = 0;int temp = 0;for (row = 0; row < this->vexnum; row++) {for (col = row + 1; col < this->vexnum; col++) {cout << "v" << to_string(row + 1) << "---" << "v" << to_string(col+1) << " weight: "<< this->dis[row][col] << " path: " << " v" << to_string(row + 1);temp = path[row][col];//循环输出途径的每条路径。while (temp != col) {cout << "-->" << "v" << to_string(temp + 1);temp = path[temp][col];}cout << "-->" << "v" << to_string(col + 1) << endl;}cout << endl;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • main.cpp文件的代码
#include"Floyd.h"//检验输入边数和顶点数的值是否有效,可以自己推算为啥:
//顶点数和边数的关系是:((Vexnum*(Vexnum - 1)) / 2) < edge
bool check(int Vexnum, int edge) {if (Vexnum <= 0 || edge <= 0 || ((Vexnum*(Vexnum - 1)) / 2) < edge)return false;return true;
}
int main() {int vexnum; int edge;cout << "输入图的种类:1代表有向图,2代表无向图" << endl;int kind;cin >> kind;//判读输入的kind是否合法while (1) {if (kind == 1 || kind == 2) {break;}else {cout << "输入的图的种类编号不合法,请重新输入:1代表有向图,2代表无向图" << endl;cin >> kind;}}cout << "输入图的顶点个数和边的条数:" << endl;cin >> vexnum >> edge;while (!check(vexnum, edge)) {cout << "输入的数值不合法,请重新输入" << endl;cin >> vexnum >> edge;}Graph_DG graph(vexnum, edge);graph.createGraph(kind);graph.print();graph.Floyd();graph.print_path();system("pause");return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

输入:

2
7 12
1 2 12
1 6 16
1 7 14
2 3 10
2 6 7
3 4 3
3 5 5
3 6 6
4 5 4
5 6 2
5 7 8
6 7 9 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出:

任意两点间距离最短之Floyd算法相关推荐

  1. 任意两点最短路floyd算法matlab,多源最短路——Floyd算法

    Floyd算法 问题的提出:已知一个有向网(或者无向网),对每一对定点vi!=vj,要求求出vi与vj之间的最短路径和最短路径的长度. 解决该问题有以下两种方法: (1)轮流以每一个定点为源点,重复执 ...

  2. 图的所有顶点间的最短路径(Floyd算法)

    问题描述 对每一对顶点vi ≠ vj,求出vi与vj之间的最短路径和最短路径长度 Floyd算法 Floyd(Floyd-Warshall)算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中 ...

  3. 基础算法题——城市间最短路程(Floyd算法)

    Floyd算法 通过一个图的权值矩阵求出它的每两点间的最短路径矩阵. 其状态转移方程如下:map[i,j]:=min{map[i,k]+map[k,j],map[i,j]} 问题描述 现已知有N(N& ...

  4. [图] Floyd算法|佛洛依德 - 任意两点的最短路径 - C语言实现

    文章目录 原理 核心代码 代码 path[][]数组解释 完整代码 原文链接:https://www.yuque.com/cppdev/algo/bgt4ea 原理 [Floyd算法]用动态规划的思想 ...

  5. floyd算法java_利用JAVA和Floyd算法实现上海地铁最短路线搜索系统

    [实例简介] Floyd算法基于JAVA实现上海地铁最短路线搜索系统,图形界面,支持新建删除站点以及新建线路. [实例截图] [核心代码] SUBWAY └── SUBWAY ├── bin │   ...

  6. 数据结构之图论算法(五)——关键路径(Dijkstra算法与Floyd算法)

    最短路径 1.最短路径问题 一般是求带权有向图G = (V,E)中的一个或多个点到G中其他各个点的最短路径. 源点:路径上的第一个顶点 终点:最后一个顶点 求解方法: 单源点最短路径--迪杰斯特拉(D ...

  7. 数据结构与算法(7-4)最短路径(迪杰斯特拉(Dijkstra)算法、弗洛伊德(Floyd)算法)

    目录 一.最短路径概念 二.迪杰斯特拉(Dijkstra)算法(单源最短路径) 1.原理 2.过程 3.代码 三.弗洛伊德(Floyd)算法(多源最短路径) 1.原理 2.存储 3.遍历 4.代码 参 ...

  8. 【图论专题】Floyd算法及其扩展应用

    Floyd的拓展应用: 任意两点最短路 传递闭包 找最小环 恰好经过k条边的最短路(倍增) 题目列表: 题目 算法 AcWing 1125. 牛的旅行 任意两点最短路Floyd AcWing 343. ...

  9. 【图论】用一道题从本质上讲清楚Floyd算法

    P1119 [灾后重建] 4 5 1 2 3 4 0 2 1 2 3 1 3 1 2 2 1 4 0 3 5 4 2 0 2 0 1 2 0 1 3 0 1 4 -1 -1 5 4 一道非常好的Flo ...

最新文章

  1. 分布式锁用 Redis 还是 Zookeeper?
  2. re.search中与正则表达式*结合使用的注意事项
  3. 【mybatis】xml中定义变量
  4. 64位Ubuntu kylin 16.04使用fastboot下载内核到tiny4412开发板
  5. 数据库和Webapp安全
  6. c#委托调用另一窗口函数_在C#中使用委托调用成员函数
  7. WebStorm汉化包下载(以及Android Studio,IntelliJIDEA等的汉化包下载)
  8. 凤凰android root x86,凤凰OS 3.0.5版 root教程
  9. YUV的原始数据文件转rgb使用cv2显示
  10. 百度网盘客户端(java)版本
  11. 精灵图(雪碧图)一招解决定位难问题,再也不用猜位置或者ps测距离了
  12. 计算机电脑的时区怎么弄,电脑时区怎么设置,教你win10中国时区UTC设置教程
  13. 整合springmvc+mybatis+veloctiy三
  14. [CC2642r1] ble5 stacks 蓝牙协议栈 介绍和理解 TI协议栈下载
  15. 软件能力成熟度模型(Capabilitymaturity model,CMM)
  16. 微信jsapi支付结果回调错误Tag mismatch
  17. css美观文本框_如何为美观和用户友好的文本使用类型
  18. R语言,直方图的制作--hist()
  19. 【Gamma】Scrum Meeting 1 与助教谈话
  20. mfrc522 c语言程序,用mxgxw/MFRC522python库和MFRC522读卡器在raspi2上编写ntag213(Mifare ultralightc)...

热门文章

  1. 蓝的成长记——追逐DBA(12):七天七收获的SQL
  2. sentry php使用,Sentry简介
  3. iframe标签使用传递参数
  4. 编码、编码器和二进制编码器
  5. spooling技术课程设计 linux环境,SPOOLING技术模拟实现 操作系统原理课程设计.doc
  6. QT编程:复选框QCheckBox
  7. C++之函数重载重铸版
  8. 使用Python中的tkinter模块实现GUI界面的学生信息管理系统
  9. pypy3与python多线程比较
  10. python协同过滤_协同过滤的python简易实现