当前位置:   article > 正文

最短路径---Floyd算法(C++)_最短路径问题c++

最短路径问题c++

Floyd算法的介绍
算法的特点: 
弗洛伊德算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。

算法的思路

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

假设图G中顶点个数为N,则需要对矩阵D和矩阵P进行N次更新。初始时,矩阵D中顶点a[i][j]的距离为顶点i到顶点j的权值;如果i和j不相邻,则a[i][j]=∞,矩阵P的值为顶点b[i][j]的j的值。 接下来开始,对矩阵D进行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]”,更新b[i][j]=b[i][0]。 同理,第k次更新时,如果”a[i][j]的距离” > “a[i][k-1]+a[k-1][j]”,则更新a[i][j]为”a[i][k-1]+a[k-1][j]”,b[i][j]=b[i][k-1]。更新N次之后,操作完成!

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算法是如何工作的了,他每次都会选择一个中介点,然后,遍历整个矩阵,查找需要更新的值,下面还剩下五步,就不继续演示下去了,理解了方法,我们就可以写代码了。
 

代码:

floyd.h

  1. #ifndef FLOYD_H
  2. #define FLOYD_H
  3. #pragma once
  4. #include <iostream>
  5. #include <string>
  6. using namespace std;
  7. class Graph_DG
  8. {
  9. private:
  10. int vexnum; // 图中顶点个数
  11. int edge; // 图的边数
  12. int **arc; // 邻接矩阵
  13. int **dis; // 记录各个顶点最短路径的信息
  14. int **path; // 记录各个最短路径
  15. public:
  16. Graph_DG(int v, int e);
  17. ~Graph_DG();
  18. // 判断每次输入的边是否合法,顶点从1开始编号
  19. bool check_edge_value(int start, int end);
  20. void creatGraph(int kind);
  21. void print(); // 打印邻接矩阵
  22. void Floyd();
  23. void print_path(); // 打印最短路径
  24. };
  25. #endif // FLOYD_H

 

floyd.cpp

  1. #include "floyd.h"
  2. Graph_DG::Graph_DG(int v, int e)
  3. {
  4. vexnum = v;
  5. edge = e;
  6. arc = new int*[vexnum];
  7. dis = new int*[vexnum];
  8. path = new int*[vexnum];
  9. for (int i = 0; i < vexnum; ++i)
  10. {
  11. arc[i] = new int[vexnum];
  12. dis[i] = new int[vexnum];
  13. path[i] = new int[vexnum];
  14. for (int j = 0; j < vexnum; ++j)
  15. {
  16. // 邻接矩阵初始化为无穷大
  17. arc[i][j] = INT_MAX;
  18. }
  19. }
  20. }
  21. Graph_DG::~Graph_DG()
  22. {
  23. for (int i = 0; i < vexnum; ++i)
  24. {
  25. delete [] this->arc[i];
  26. delete [] this->dis[i];
  27. delete [] this->path[i];
  28. }
  29. delete [] arc;
  30. delete [] dis;
  31. delete [] path;
  32. }
  33. // 判断我们每次输入的的边的信息是否合法
  34. //顶点从1开始编号
  35. bool Graph_DG::check_edge_value(int start, int end)
  36. {
  37. if (start < 1 || end < 1 || start > vexnum || end > vexnum) // Floyd算法,权值可以为负
  38. {
  39. return false;
  40. }
  41. return true;
  42. }
  43. void Graph_DG::creatGraph(int kind)
  44. {
  45. cout << "请输入每条边的起点和终点(顶点编号从1开始)以及其权重" << endl;
  46. int start, end, weight;
  47. int count = 0;
  48. while (count != edge)
  49. {
  50. cin >> start >> end >> weight;
  51. while (!check_edge_value(start, end))
  52. {
  53. cout << "输入的边的信息不合法,请重新输入" << endl;
  54. cin >> start >> end >> weight;
  55. }
  56. arc[start-1][end-1] = weight;
  57. // 无向图添加这一句
  58. if (kind == 2)
  59. {
  60. arc[end-1][start-1] = weight;
  61. }
  62. ++count;
  63. }
  64. }
  65. void Graph_DG::print()
  66. {
  67. cout << "图的邻接矩阵为:" << endl;
  68. int row = 0;
  69. int col = 0;
  70. while (row != vexnum)
  71. {
  72. col = 0;
  73. while (col != vexnum)
  74. {
  75. if (arc[row][col] == INT_MAX)
  76. {
  77. cout << "∞ ";
  78. }
  79. else
  80. {
  81. cout << arc[row][col] << " ";
  82. }
  83. ++col;
  84. }
  85. cout << endl;
  86. ++row;
  87. }
  88. }
  89. void Graph_DG::Floyd()
  90. {
  91. int row, col;
  92. for (row = 0; row < vexnum; ++row)
  93. {
  94. for (col = 0; col < vexnum; ++col)
  95. {
  96. // 把矩阵D初始化为邻接矩阵
  97. dis[row][col] = arc[row][col];
  98. // 矩阵P的初值为各个边的终点顶点下标
  99. path[row][col] = col;
  100. }
  101. }
  102. // 三重循环,用于计算每两个点之间的最短路径.【动态规划的思想】
  103. int temp, select;
  104. for (temp = 0; temp < vexnum; ++temp)
  105. {
  106. for (row = 0; row < vexnum; ++row)
  107. {
  108. for (col = 0; col < vexnum; ++ col)
  109. {
  110. // 为防止溢出,引入一个select值
  111. select = (dis[row][temp] == INT_MAX || dis[temp][col] == INT_MAX) ?
  112. INT_MAX : dis[row][temp] + dis[temp][col];
  113. if (dis[row][col] > select)
  114. {
  115. // 更新D矩阵
  116. dis[row][col] = select;
  117. // 更新P矩阵
  118. path[row][col] = path[row][temp];
  119. }
  120. }
  121. }
  122. }
  123. }
  124. void Graph_DG::print_path()
  125. {
  126. cout << "各个顶点对的最短路径:" << endl;
  127. int row, col, temp;
  128. for (row = 0; row < vexnum; ++row)
  129. {
  130. for (col = row + 1; col < vexnum; ++col)
  131. {
  132. cout << "v" << to_string(row + 1) << "---v" << to_string(col + 1) << " weight: "
  133. << dis[row][col] << " path: v" << to_string(row + 1);
  134. temp = path[row][col];
  135. // 循环输出途径的每条路径
  136. while (temp != col)
  137. {
  138. // path[i][j] = k, 表示从i走到j,第一步需要从i到k
  139. // 同理,再从k到j,第一步需要走到path[k][j]
  140. cout << "-->v" << to_string(temp + 1);
  141. temp = path[temp][col];
  142. }
  143. cout << "-->v" << to_string(col + 1) << endl;
  144. }
  145. cout << endl;
  146. }
  147. }

 

main.cpp

  1. #include <floyd.h>
  2. //顶点数和边数的关系是:((Vexnum*(Vexnum - 1)) / 2) < edge
  3. bool check(int vexnum, int edge)
  4. {
  5. if (vexnum <= 0 || edge <= 0 || (vexnum*(vexnum-1)/2) < edge)
  6. {
  7. return false;
  8. }
  9. return true;
  10. }
  11. int main()
  12. {
  13. int vexnum, edge, kind;
  14. cout << "输入图的种类:1代表有向图,2代表无向图" << endl;
  15. cin >> kind;
  16. while (1)
  17. {
  18. if (kind == 1 || kind == 2)
  19. {
  20. break;
  21. }
  22. else
  23. {
  24. cout << "输入的图的种类编号不合法,请重新输入:1代表有向图,2代表无向图" << endl;
  25. cin >> kind;
  26. }
  27. }
  28. cout << "输入图的顶点个数和边的条数:" << endl;
  29. cin >> vexnum >> edge;
  30. while(!check(vexnum, edge))
  31. {
  32. cout << "输入的数值不合法,请重新输入" << endl;
  33. cin >> vexnum >> edge;
  34. }
  35. Graph_DG graph(vexnum, edge);
  36. graph.creatGraph(kind);
  37. graph.print();
  38. graph.Floyd();
  39. graph.print_path();
  40. return 0;
  41. }

运行结果:

 

注:

  1. 原文代码中,析构函数部分delete后面没有加 [] ,会出现内存泄漏的情况。
  2. Floyd算法可以对负权值图进行求解,若出现负环,则原问题本身无法求解,因此Floyd算法也可用于检验是否出现负环(初始化所有dis[i][i] = 0,在第三层循环relax结束后,加一个判断dis[i][i]是否为负,若为负则说明存在负环,详细代码可参考下一篇博客)。
  3. 在打印路径部分,对path[i][j]的理解如下:path[i][j] = k, 表示从i走到j,第一步需要从i到k;同理,再从k到j,第一步需要走到path[k][j]。
  4. Floyd算法本身采用的是动态规划的思想。

 

参考资料:https://blog.csdn.net/qq_35644234/article/details/60875818

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号