迪杰斯特拉算法介绍

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个结点到其他结点的最短路径。 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止

思路

  1. 选一个节点作为起始点v
  2. 引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度,可以再用一个集合装线路),U集合包含未求出最短路径的点,U表示起始点到这些点的距离
  3. 初始化两个集合,S集合初始时 只有当前要计算的节点v,U中是除v以外的顶点
  4. 从U中找出路径最短的点加入S
  5. 更新U集合,U里面计算起始点到这些点的距离,上一个放入S的点到某一个点的距离+起始点到上一个点距离<起始点到某一个点的距离就跟更新U,详细看图解第二步
  6. 循环4,5步,直到遍历结束,得到A到其他节点的最短路径

    图解

    迪杰斯特拉(Dijkstra)算法 - 图1

  7. 以D为起始点,第一步选取D点

    S包括D[0] U包括A[x],B[x],C[3],E[4],F[x],G[x]

注:

  • S是已计算出最短路径的顶点的集合
  • U是未计算出最短路径的顶点的集合
  • C[3]表示顶点C到起点D的最短路径是3

image.png

  1. 选择C,因为到C点是U中路径最短的

    现在S包括D[0],C[3] U则进行调整,A[x],B[13],E[4],F[9],G[x] U表示起始点到这些点的距离。起始点到某点的距离,就是上一个放入S的点到某点的距离加上起始点到上一个放入S点的距离。 例如F点,这里U里面的距离变成了9,表示上一个放入S的点C到F的距离为6,起始点D到C的距离为3,3+6=9

image.png

  1. 选择E,因为E[4]最短

    S包括D[0],C[3],E[4] U调整为A[x],B[13],F[6],G[12]

image.png

  1. 选择F[6]

    S包括D[0],C[3],E[4],F[6] U调整为A[22],B[13],G[12]

image.png

  1. 选择G[12]

    S包含D[0],C[3],E[4],F[6],G[12] U调整为A[22],B[13]

image.png

  1. 选择B[13]

    S包含D[0],C[3],E[4],F[6],G[12],B[13] U调整为A[22]

image.png

  1. 选择A[22]

    S包含D[0],C[3],E[4],F[6],G[12],B[13],A[22] U没有了

image.png

代码

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. using System.Text;
  7. namespace ConsoleApp1
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. char[] vertexs = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
  14. //邻接矩阵的关系使用二维数组
  15. //比如C和G之间不连通,就用8888来表示,权值太大就不考虑了
  16. int x = 8888;
  17. int[,] matrix = new int[,]
  18. {
  19. //A B C D E F G
  20. /*A*/{0 , 12, x , x , x , 16, 14 },
  21. /*B*/{12, 0 , 10, x , x , 7 , x },
  22. /*C*/{x , 10, 0 , 3 , 5 , 6 , x },
  23. /*D*/{x , x , 3 , 0 , 4 , x , x },
  24. /*E*/{x , x , 5 , 4 , 0 , 2 , 8 },
  25. /*F*/{16, 7 , 6 , x , 2 , 0 , 9 },
  26. /*G*/{14, x , x , x , 8 , 9 , 0 }
  27. };
  28. dijkstra(matrix, vertexs, 0);
  29. }
  30. static void dijkstra(int[,] weight,char[] vertexs, int start)
  31. {
  32. int n = vertexs.Length;//顶点个数
  33. int[] shortPath =new int[n];//保存start点到其他顶点的最短路径
  34. string[] path =new string[n];//保存start点到其他顶点的最短路径的字符串表达方式,比如A->B
  35. for (int i = 0; i < n; i++)//字符串表达式
  36. path[i] = $"{vertexs[start]}-->{vertexs[i]}";
  37. int[] visited =new int[n];//标记当前的最短路径是否已经求出来了,1表示求出来了
  38. //初始化顶点,start到start的距离当前是0啊,默认start到start的距离就是已经访问过了
  39. shortPath[start] = 0;
  40. visited[start] = 1;
  41. for(int i = 1; i < n; i++)//start已经访问过了,剩余要加入的顶点就是n-1
  42. {
  43. int k = -1;//选出一个距离初始顶点start最近的未标记顶点
  44. int dmin = 8888;//这个是最短路径,先用一个大数占位
  45. for(int j=0;j < n; j++)
  46. {
  47. //选出一个距离初始顶点start最近的未标记顶点
  48. if (visited[j] == 0 && weight[start, j] < dmin)
  49. {
  50. Console.WriteLine($"i={i},j={j},weight[{start},{j}]={weight[start, j]},dmin={dmin}");
  51. dmin =weight[start, j];
  52. k = j;
  53. }
  54. }
  55. //将新选出的顶点标记为已求出的最短路径,且到start的最短路径就是dmin
  56. shortPath[k] = dmin;
  57. visited[k] = 1;
  58. //以k为中间点,修正从start到未访问各点的距离
  59. for(int j=0;j<n; j++)
  60. {
  61. //核心
  62. //如果 起始点到当前点的距离+当前点到某点的距离<起始点到某点的距离,则更新
  63. //比如 A到G,14,加上G到E,8,等于22。A是起始点,G是当前点。而A到E的距离则为8888,因为不连通。
  64. //所以AG+GB<AE,更新A到E的最短距离为22
  65. if(visited[j] == 0 && weight[start, k] + weight[k, j] < weight[start, j])
  66. {
  67. weight[start, j] = weight[start, k] + weight[k, j];
  68. path[j] = path[k] + "-->" + vertexs[j];
  69. }
  70. }
  71. }
  72. for(int i = 0; i < n; i++)
  73. {
  74. Console.WriteLine($"从{vertexs[start]}出发到{vertexs[i]}的最短路径为:{path[i]}");
  75. }
  76. }
  77. }
  78. }

输出

image.png