【算法】图的最短路径(Dijkstra算法)

逃离我推掉我的手 2022-06-08 03:59 442阅读 0赞
  1. 今天要总结的是图的迪杰斯特拉算法。这个算法是针对有**向带权图**的,求的是**图中某一个定点到其余所有顶点的最短路径**。

下面说说这个算法的基本思想吧:设定两个集合A和B,A中存放我们已经处理过的顶点,B中存放图中剩余顶点。刚开始的时候,A中只有一个我们选定的起点v0,每一次从集合B中取到v0的代价最小的点并入,每一次并入时都需要修改v0到B中顶点的代价,直到所有的顶点都并入为止。

算法准备:

1、数组dist,用于存放v0到图中个顶点的代价,数组下标表示顶点编号

2、数组path,用于存放路径,数组下标表示顶点编号,下标所对应的数组值表示能够到达当前这个顶点的前一个顶点编号,最后连起来就是v0到图中各顶点的最短路径了。如果没有前前驱顶点,则内容值为-1。

3、数组set,用于标识图中顶点是否被处理过,数组下标表示顶点编号。处理过为1,没处理过为0。

4、使用图的邻接矩阵来存储有向带权图,graph[i][j]。

算法过程(我们选择编号为0的顶点作为起点):

1、首先进行3个数组的初始化,把set数组全部初始化为0;dist数组全部初始化为无穷大,path数组全部初始化为-1。

2、将set[0]的值设置为1,然后遍历邻接矩阵的第0行,依次更新dist数组的每一项。

3、将dist数组中值不为无穷大的在path中的对应下标,把它们的值改为0。因为编号为0的点是它们的前驱嘛。如下图:

20170914091735522

4、选择dist数组中值最小的点的下标,这里为1。

5、将set[1]的值设置为1

6、遍历邻接矩阵的第1行(因为1是最小权值的下标),将dist[1]的值与邻接矩阵graph[1][i]的值相加(此时是以编号为1的点作为中间点,看看由v0->v1再到其余点的路径长度会不会比v0直接到它们的路径长度短),如果这个值比dist[i]的值小,就更新dist[i],同时将path[i]的值设置为1,执行这个操作的前提是,set[i]为0。操作完成后如下图:

20170914092731172

7、重复【4】~【6】步,如果已经处理过的点就不用再判断了。直到set数组全变为1。最后如下图

20170914093705513

现在,我们就可以看出最短路径了。

比如v0到v6的路径为,注意path数组,从下标6的位置开始看起,逐级向上查找,直到遇到-1为止

v6->v4->v5->v2->v1->v0

有没有发现这个是逆序的呢?根据后进先出的原则,我们想到输出要用到栈。

下面上代码:

1、首先是图的结构体定义

  1. #define Max 100
  2. typedef struct graph *Graph;
  3. typedef struct graph
  4. {
  5. int data[30][30];
  6. int n , e;
  7. }graph;

2、接下来就是算法核心代码

  1. void Dijkstra(Graph g , int v , int path[] , int dist[])
  2. {
  3. int set[Max];
  4. //initial
  5. int i , j;
  6. for(i = 0 ; i < g->n ; i++)
  7. {
  8. set[i] = 0;
  9. dist[i] = g->data[v][i];
  10. if(g->data[v][i] < Max)
  11. {
  12. path[i] = v;
  13. }
  14. else
  15. {
  16. path[i] = -1;
  17. }
  18. }
  19. set[0] = 1;
  20. path[0] = -1;
  21. //main
  22. for(i = 1 ; i<= g->n-1 ; i++)
  23. {
  24. int min = Max;
  25. int minP = 0;
  26. for(j = 0 ; j < g->n ; j++)
  27. {
  28. if(set[j] == 0 && dist[j] < min)
  29. {
  30. min = dist[j];
  31. minP = j;
  32. }
  33. }
  34. set[minP] = 1;
  35. for(j = 0 ; j < g->n ; j++)
  36. {
  37. if(set[j] == 0 && g->data[minP][j] + min < dist[j])
  38. {
  39. dist[j] = g->data[minP][j] + min;
  40. path[j] = minP;
  41. }
  42. }
  43. }
  44. }

注:a、参数列表中的变量v就是我们指定的定点。

  1. b、在初始化path数组的过程中,如果某个顶点在邻接矩阵v行的值不为无穷大,说明该点可以由v到达,所以将该path数组中的值设置成v,而在矩阵中v行值为无穷大的,就设置成-1

3、输出路径代码

  1. void showPath(int path[], int begin)
  2. {
  3. int stack[Max];
  4. int top = -1;
  5. int i = begin;
  6. int flag = 0;
  7. while(path[begin] != -1)
  8. {
  9. stack[++top] = begin;
  10. begin = path[begin];
  11. }
  12. stack[++top] = begin;
  13. if(top == 0)
  14. {
  15. printf("v%d 无最短路径",stack[top]);
  16. return;
  17. }
  18. while(top != -1)
  19. {
  20. if(!flag)
  21. {
  22. printf("v%d",stack[top]);
  23. flag++;
  24. }
  25. else
  26. printf("->v%d",stack[top]);
  27. top--;
  28. }
  29. }

如果没有最短路径的话,说明这个点就没有前驱了,都是-1,在路径序列中只有它自己。

发表评论

表情:
评论列表 (有 0 条评论,442人围观)

还没有评论,来说两句吧...

相关阅读

    相关 路径Dijkstra算法

    最短路径之Dijkstra算法(看到i,j,k三个变量可以理解为需要三个for循环,方便记忆) 本节来学习指定一个点(源点)到其余各个顶点的最短路径,也称为”单源最短路径”。

    相关 路径Dijkstra算法 java

    思路:设置一个基点集合 S ,并不断地作贪心选择来扩充这个集合。一个顶点属于集合 S 当且仅当从源到该顶点的最短路径长度已知。初始时,S中仅含有源。设 u 是 G 的某一个顶点