原理

【Floyd算法】用动态规划的思想求任意两点之间的最短路径
【评价】时间复杂度Floyd(任意两点) - 图1#card=math&code=O%28n%5E3%29&id=rvoye),空间复杂度Floyd(任意两点) - 图2#card=math&code=O%28n%5E2%29&id=DYSEn),但形式上比Dijkstra简单些,也容易理解
【基本思想】以二维数组D[v][w]保存v->w的最短路径长度

  1. 【初始状态】D[v][w] —> 没有中间点,v直接走到w的距离
  2. 【分别考虑所有结点u】把u看成中转站,检查v->u->w(即D[v][u]+D[u][w])是否比v->w(即D[v][w])小,如果小就更新D[v][w]的值

【当然】我们还需要一个path[v][w]的二维数组来保存路径(path[][]详细解释请看下文)

【主要公式】

  1. // D[v][w]存储v->w的最短路径长度
  2. // v->w中间可能经过很多点
  3. if (D[v][u] + D[u][w] < D[v][w]) { // v->u->w的长度 < 当前v->w的长度
  4. D[v][w] = D[v][u] + D[u][v]; //那么最短路径D[v][w]即是 v->u->w的距离
  5. path[i][j] = v; //i->j的路径上,有中转站v
  6. }

【算法示例】
Floyd(任意两点) - 图3

核心代码

  1. void PrintPath(int u, int v, int path[][maxSize]) {
  2. int mid;
  3. if (path[u][v]==-1) {
  4. //直接输出,没有中间点
  5. printf("<%c,%c> ", vertexs[u], vertexs[v]);
  6. } else { //有中间点
  7. mid = path[u][v];
  8. PrintPath(u, mid, path);
  9. PrintPath(mid, v, path);
  10. }
  11. }
  12. void Floyd(int n, int MGraph[][maxSize], int path[][maxSize]) {
  13. int i,j,v;
  14. int A[maxSize][maxSize]; //最短路径
  15. //初始化:A-1没有考虑中间节点
  16. for (i=0; i<n; ++i) {
  17. for (j=0; j<n; j++) {
  18. A[i][j] = MGraph[i][j];
  19. path[i][j] = -1;
  20. }
  21. }
  22. //循环考虑中间节点
  23. for (v=0; v<n; ++v) {
  24. for (i=0; i<n; ++i) {
  25. for (j=0; j<n; ++j) {
  26. if (A[i][j]>A[i][v]+A[v][j]) {
  27. A[i][j] = A[i][v] + A[v][j];
  28. path[i][j] = v;
  29. }
  30. }
  31. }
  32. }
  33. }

代码

测试数据 结果 0->3的最短路径
Floyd(任意两点) - 图4 Floyd(任意两点) - 图5 0到3的最短路径为
0->5->4->3
A->F->E->D

path[][]数组解释

结点 A B C D E F G
下标 0 1 2 3 4 5 6
A 0 1 -1 1 5 5 -1 -1
B 1 -1 -1 2 -1 2 3 6
C 2 1 -1 1 -1 3 4 1
D 3 5 2 -1 4 -1 4 -1
E 4 5 3 3 -1 5 -1 5
F 5 -1 6 4 4 -1 4 -1
G 6 -1 -1 1 -1 5 -1 3

【path[u][v]的值】u->v的最短路径中,有中转站path[u][v]
【特殊值】path[u][v]=-1:表示u到v之间没有中间节点—>u到v的最短路径即是边u->v
【求法】求0到3的最短路径

  1. path[0][5]=5 --> 0到3的路上有中转站5,即0->5->3
  2. 【0->5->3前半段】path[0][5]=1 --> 0到5的路上没有中转站了,前半段即0->5
  3. 【0->5->3后半段】path[5][3]=4 --> 5到3的路上有中转站4,后半段5->4->3
    1. 【5->4->3前半段】path[5][4]=-1 --> 没有中转站了,即5->4
    2. 【5->4->3后半段】path[4][3]=-1 --> 没有中转站,即4->3

【综上】0到3的最短路径为0->5->4->3,即A->F->E->D

完整代码

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define maxSize 10
  4. #define INF 100
  5. int MGraph[maxSize][maxSize]; //邻接矩阵
  6. char vertexs[maxSize]; //结点信息
  7. void PrintPath(int u, int v, int path[][maxSize]) {
  8. int mid;
  9. if (path[u][v]==-1) {
  10. //直接输出,没有中间点
  11. printf("<%c,%c> ", vertexs[u], vertexs[v]);
  12. } else { //有中间点
  13. mid = path[u][v];
  14. PrintPath(u, mid, path);
  15. PrintPath(mid, v, path);
  16. }
  17. }
  18. void Floyd(int n, int MGraph[][maxSize], int path[][maxSize]) {
  19. int i,j,v;
  20. int A[maxSize][maxSize]; //最短路径
  21. //初始化:A-1没有考虑中间节点
  22. for (i=0; i<n; ++i) {
  23. for (j=0; j<n; j++) {
  24. A[i][j] = MGraph[i][j];
  25. path[i][j] = -1;
  26. }
  27. }
  28. //循环考虑中间节点
  29. for (v=0; v<n; ++v) {
  30. for (i=0; i<n; ++i) {
  31. for (j=0; j<n; ++j) {
  32. if (A[i][j]>A[i][v]+A[v][j]) {
  33. A[i][j] = A[i][v] + A[v][j];
  34. path[i][j] = v;
  35. }
  36. }
  37. }
  38. }
  39. }
  40. int main() {
  41. /*
  42. 7
  43. ABCDEFG
  44. 10000 18 10000 10000 10000 19 18
  45. 18 10000 8 10000 10000 10000 20
  46. 10000 8 10000 20 10000 10000 10000
  47. 10000 10000 20 10000 9 16 15
  48. 10000 10000 10000 9 10000 3 10000
  49. 19 10000 10000 16 3 10000 15
  50. 18 20 10000 15 10000 15 10000
  51. 0 2
  52. 0 3
  53. 0 4
  54. 1 6
  55. 4 6
  56. 3 6
  57. */
  58. int n;
  59. int i,j;
  60. char tmp[maxSize+5];
  61. int path[maxSize][maxSize];
  62. int start,end;
  63. scanf("%d", &n); //结点数
  64. scanf("%s", tmp); //结点信息
  65. for (i=0; i<n; i++)
  66. vertexs[i] = tmp[i];
  67. for (i=0; i<n; i++) { //矩阵
  68. for (j=0; j<n; j++) {
  69. scanf("%d", &MGraph[i][j]);
  70. }
  71. }
  72. Floyd(n, MGraph, path);
  73. //打印path数组
  74. printf("- path数组\n");
  75. printf("结点");
  76. for (i=0; i<n; i++) {
  77. printf("\t%c", vertexs[i]);
  78. }
  79. printf("\n");
  80. for (i=0; i<n; i++) {
  81. printf("%c\t", vertexs[i]);
  82. for (j=0; j<n; j++) {
  83. printf("%d\t", path[i][j]);
  84. }
  85. printf("\n");
  86. }
  87. //输出最短路径
  88. while (1) {
  89. printf("\n\n>>> 输入起始点的下标(空格间隔):");
  90. scanf("%d %d" , &start, &end); //输入两个测试的顶点,求v->w的最短路径
  91. printf("%c->%c最短路径:", vertexs[start], vertexs[end]);
  92. PrintPath(start, end, path);
  93. }
  94. return 0;
  95. }