数据结构课设——校园导航系统第二天
弗洛伊德算法——求权值最短路线
(一)算法思想:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点 i 到点 j 的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)。
从任意节点 i 到任意节点 j 的最短路径不外乎2种可能,一是直接从i到j,二是从i经过若干个节点k到 j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
(二)算法过程
1)首先把初始化距离dist数组为图的邻接矩阵,路径数组path初始化为-1。其中对于邻接矩阵中的数首先初始化为正无穷,如果两个顶点存在边则初始化为权重
2)对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是就更新它。
状态转移方程为
如果 dist[i][k]+dist[k][j] < dist[i][j]
则dist[i][j] = dist[i][k]+dist[k][j]
Dijikstra算法
Dijikstra算法主要是针对有权图的最短路径问题提出的,且具体问题中不能出现权值为负的边,即负值圈问题,
对于Dijikstra算法的理解,首先得从最短路径的最优子结构说起。(这部分引用海子的博客园的Dijkstra算法(单源最短路径)一文的说法)
最短路径的最优子结构性质
该性质描述为:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。
假设P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P(k,s),那么P(i,j)=P(i,k)+P(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。
那么Dijikstra算法描述如下:
假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。
1)从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2)更新与i直接相邻顶点的dist值。dist[j]=min{dist[j],dist[i]+matrix[i][j]}
3)直到U=V,算法停止。
起始Dijikstra算法的本质就是贪心算法。
1 //14.查询两景点间的最短路径(floyd算法) 2 void floyd(mgraph c)//一种暴力破解获取最短路径的算法 3 { int i,j,k; 4 for(i=1;i<=key;i++)//将图的邻接矩阵赋值给 shortest二维数组,将矩阵pathh全部初始化为-1 5 { for(j=1;j<=key;j++) 6 { shortest[i][j]=c.arcs[i][j].adj; 7 pathh[i][j]=j; 8 } 9 } 10 /*int i1,j1,k1=0; 11 for(i1=1;i1<=key;i1++) 12 printf("%6d",i1);//横着的标号1到11 13 printf("\n"); 14 for(i1=1;i1<=key;i1++) 15 { printf("%d",i1);//竖着的标号1到11 16 for(j1=1;j1<=key;j1++) 17 { 18 printf("%6d",pathh[i1][j1]); 19 k1++; 20 if(k1%key==0) printf("\n"); 21 } 22 } 23 printf("\n\n\n");*/ 24 25 for(k=1;k<=key;k++)//核心操作,完成了以k为中间点对所有的顶点对(i,j)进行检测和修改 26 { for(i=1;i<=key;i++) 27 { for(j=1;j<=key;j++) 28 { 29 if(shortest[i][j]>shortest[i][k]+shortest[k][j]) 30 { shortest[i][j]=shortest[i][k]+shortest[k][j]; 31 pathh[i][j]=pathh[i][k];//记录一下所走的路 //P数组用来存放前驱顶点 32 } 33 } 34 } 35 } 36 } 37 /*void printf_Pshuzu() //输出前驱矩阵 38 { int i,j,k=0; 39 for(i=1;i<=key;i++) 40 printf("%6d",i);//横着的标号0到11 41 printf("\n"); 42 for(i=1;i<=key;i++) 43 { printf("%d",i);//竖着的标号0到11 44 for(j=1;j<=key;j++) 45 { printf("%6d",pathh[i][j]); 46 k++; 47 if(k%key==0) printf("\n"); 48 } 49 } 50 printf("\n\n\n"); 51 }*/
1 //打印出最短路径 2 void display(mgraph c, int i, int j) 3 { 4 int a, b; 5 a = i; 6 b = j; 7 printf("您要查询的两景点间最短路径:\n\n"); 8 printf("%d%s", a, c.vexs[a].name); 9 while (pathh[i][j] != b) 10 { 11 printf("-->%d%s", pathh[i][j], c.vexs[pathh[i][j]].name); 12 i = pathh[i][j]; 13 } 14 printf("-->%d%s\n\n", b, c.vexs[b].name); 15 printf("%s-->%s的最短路径是:%d 米。\n\n", c.vexs[a].name, c.vexs[b].name, shortest[a][b]); 16 } 17 //任意两点间最短距离(弗洛伊德算法) 18 int shortdistance(mgraph c) 19 { 20 int i, j; 21 printf("请输入要查询的两个景点的数字编号(1->11)中间用空格间隔开。\n"); 22 scanf("%d %d", &i, &j); 23 if (i > key || i < 0 || j > key || j < 0) 24 { 25 printf("输入信息错误!\n\n"); 26 printf("请输入要查询的两个景点的数字编号(1->11)中间用空格间隔开。\n"); 27 scanf("%d %d", &i, &j); 28 } 29 else 30 { 31 floyd(c); //printf_Pshuzu(); 32 display(c, i, j); 33 } 34 return 1; 35 }
1 //用迪杰斯特拉算法,求出一个景点到其他景点间的最短路径, 2 void shortestpath_dij(mgraph c) 3 { 4 int v0, v, w, k = 1, min, t, p; 5 int final[MaxVertexNum]; //final[w]=1表示已经求得顶点V0到Vw的最短路径 6 int Patharc[MaxVertexNum]; //用于存储最短路径下标的数组 7 int ShortPathtable[MaxVertexNum]; //用于存储到各点最短路径的权值和 8 printf("\n请输入一个起始景点的编号:"); 9 scanf("%d", &v0); 10 printf("\n\n"); 11 while (v0 < 0 || v0 > key) 12 { 13 printf("\n您输入的景点编号不存在\n"); 14 printf("请重新输入:"); 15 scanf("%d", &v0); 16 } 17 //初始化数据 18 for (v = 1; v <= c.vexnum; v++) //数组0还是空出来 19 { 20 final[v] = 0; //全部顶点初始化为未找到最短路径 21 ShortPathtable[v] = c.arcs[v0][v].adj; //将与v0点有连线的顶点加上权值 22 Patharc[v] = 0; //初始化路径数组p为0 23 } 24 ShortPathtable[v0] = 0; // V0至v0的路径为0 25 final[v0] = 1; //V0至v0不需要路径 26 //开始主循环,每次求得V0到某个v顶点的 最短路径 27 for (v = 1; v <= c.vexnum; v++) 28 { 29 min = Infinity; 30 for (w = 1; w <= c.vexnum; w++) //找出最近的顶点和权值 31 { 32 if (!final[w] && ShortPathtable[w] < min) //有边 33 { 34 k = w; 35 min = ShortPathtable[w]; 36 } 37 } 38 final[k] = 1; //将目前找到的最近的顶点置1 39 //修正当前最短路径及距离 40 for (w = 1; w <= c.vexnum; w++) 41 { //如果经过v顶点的路径比现在这条路径的长度短的话,更新 42 if (!final[w] && (min + c.arcs[k][w].adj < ShortPathtable[w])) 43 { 44 ShortPathtable[w] = min + c.arcs[k][w].adj; //修改当前路径长度 45 Patharc[w] = k; //存放前驱结点(像糖葫芦) 46 } 47 } 48 } 49 50 /*//打印P数组 51 printf("打印P数组:"); 52 for(t=1;t<=c.vexnum;t++) 53 { 54 printf("%d ",Patharc[t]); 55 } 56 printf("\n\n"); 57 //打印s数组 58 printf("打印S数组:"); 59 for(t=1;t<=c.vexnum;t++) 60 { 61 printf("%d ",ShortPathtable[t]); 62 } 63 printf("\n\n");*/ 64 65 //打印最短路径 66 for (t = 1; t <= c.vexnum; t++) 67 { 68 p = t; 69 if (t != v0) //反向输出 70 { 71 printf("%d%s", t, c.vexs[t].name); 72 for (w = 1; w <= c.vexnum; w++) 73 { 74 if (Patharc[p] != 0) 75 { 76 printf("<--%d%s", Patharc[p], c.vexs[p].name); 77 p = Patharc[p]; 78 } 79 } 80 printf("<--%d%s", v0, c.vexs[v0].name); 81 printf("\n总路线长为%d米\n\n", ShortPathtable[t]); 82 } 83 } 84 }