【学习笔记----数据结构20-图的最短路径】

最短路径

对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

迪杰斯特拉算法


它并不是一下子就求出V0到V8的最短路径,而一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到你要的结果。

我们来看一下思路

1.    顶点v0到v1的最短距离,答案是1,路径就是直接v0连线到v1

2.    由于顶点v1还与v2、v3、v4连线,所以

我们同时求得了

v0->v1->v2=1+3=4,

v0->v1->v3=1+7=8

v0->v1->v4=1+5=6

现在,问v0到v2的最短距离是?答案是4


由于顶点v2还与v4、v5连线,所以此时我们同时求得v0->v2->v4 = 4+1=5

v0->v2->v5=4+7=11。这里v0->v2我们用的是刚才计算出来 的较小的4。此时我们也发现v0->v1->v2->v4=5要比v0->v1->v4=6还要小。所以v0到v4目前的最小距离是5。如图

 

当我们要求v0到v3的最短距离时,通向v3有三条边,除v6没研究过外,

v0->v1->v3=结果是8

v0->v4->v3=5+2=7。因此v0到v3的最短距离是7

 

       我们来看一下代码模拟运行

#define   MAXVEX  9

#define   INFINITY   65535

typedef  int   Patharc[MAXVEX]; //用于存储 最短路径下标的数组

typedef  int   ShortPathTable[MAXVEX]; //用于存储到各点最短路径的权值

/*Dijkstra算法,求有向图G的V0顶点到其余顶点v最短路径P[v]及带权长度D[v]*/

/*P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和*/

void  ShortesPath_Dijkstra(MGraph  G,int  v0,Patharc   *P,ShortPathTable  *D)

{

      int   v,w,k,min;

     int   final[MAXVEX]; //final[w]=1表示顶点v0到vw的最短路径

      for(v=0;v<G.numVertexes;v++)

      {

          final[v]=0; //全部顶点初始化为未知最短路径状态

          (*D)[v] = G.arc[v0][v];

          (*P)[v] = 0;

      }

       (*D)[v0]=0;

       final[v0]=1;

      /**开始主循环,每次求得v0到某个v顶点的最短路径/

       for(v=1;v<G.numVertexes;v++)

       {

               min=INFIITY;

               for(w=0;w<G.numvertexes;w++)

              {

                   if(!final[w]&&(*D)[w]<min)

                      {

                            k=w;

                           min = (*D)[w];

                      }

              }

              final[k]=1; //将目前找到的最近的顶点置为1

              for(w=0;w<G.numVertexes;w++)

            {

                 if(!final[w]&&(min+G.arc[k][w]<(*D)[w]))

                  {

                        (*D)[w]=min*G.arc[k][w];

                         (*P)[w] = k;

                   }

              }

        }

}

 

分析过程:

1.    程序开始运行,第4行的final数组是为了v0到某顶点是否已经求得最短路径的标记,如果v0到vw已经有结果,则final[w]=1;

2.    第5~10行,是在对数据进行初始化的工作。此时final数组值均为0,表示所有的点都未求得最短路径。D数组为{65535,1,5,65535,65535,65535,65535,65535}。因为v0与v1和v2的边权值为1和5。P数组全是0,表示目前没有路径。

3.    第11行,表示v0到v0自身,权值和结果为0。D数组为{0,1,5,65535,65535,65535,65535,65535}。第12行,表示v0点算是已经求得最短路径,因此final[0]=1。此时final数组为{1,0,0,0,0,0,0,0,0}此时整个初始化操作完成

4.    第13行~33,为主循环,每次循环求得v0与一个顶点的最短路径。因此v从1而不是0开始。

5.    先令min=65535的极大值,通过w循环,与D[w]比较找到最小值min=1,k=1。

6.    第24行,由k=1,表示与v0最近的顶点是v1,并且由D[1]=1,适量此时v0到v1的最短距离是1。因此将v1对应final[1]设置为1。此时final数组为{1,1,0,0,0,0,0,0,0}

7.    第25~32行是一循环,此循环甚为关键。它的目的是在刚才已经找到的v0到v1的最短路径的基础上,对v1与其它顶点的边进行计算,得到v0与它们的最短距离,如图

      因为此时min=1,所以本来的D[2]=5,现在v0->v1->v2=D[2]=min+3=4;

v0->v1->v3=D[3]=min+7=8, v0->v1->v4=D[4]=min+5=6,因此D数组当前值为{0,1,4,8,6,65535, 65535, 65535, 65535}。而P[2]=1,P[3]=1,P[4]=1,它表示意思是v0到v2,v3,v4点的最短路径它们的前驱是v1。此时P数组值为{0,0,1,1,1,0,0,0,0}

 

8.    重新开始循环,此时v=2。第15~23行,对w循环,注意思为final[0]=1和final1[1]=1,由第18行的!final[w]可知,v0与v1并不参与最小值获取。通过循环比较,找到最小值min=4,k=2。

9.    第24行,由k=2,表示已经求出v0到v2的最短路径,并且由D[2]=4,知道最短距离是4。因此将v2对应的final[2]设置为1,此时final数组为{1,1,1,0,0,0,0,0,0}

10.  第25~32行。在刚才已经找到V0与v2的最短路径的基础上,对v0与其他顶点的边,进行计算,得到v0与它们的当前最短距离,如图。因为min=4,所以本来D[4]=6,现在v0->v2->v4=D[4]=min+1 = 4; v0->v2->v5=D[4]=min+7 = 11,因此D数组当前值为:{0,1,4,8,5,11, 65535, 65535, 65535}。而原来的P[4]=1而现在的P[4]=2,P[5]=2,它表示v0到v4、v5点的最短路径它们的前驱均是v2。此时P数组值为:{0,0,1,1,2,2,0,0,0}。

11.  重新开始循环,此时v=3。第15~23行,得到最小值min=5,k=4。

12.  第24行,由k=4已经求出v0到v4的最短路径,并且由D[4]=5知道最短距离是5。因此将v4对应的final[4]设置为1。此时final数组为{1,1,1,0,1,0,0,0,0}

13.  第25~32行,对v4与其他顶点的边进行计算。得到v0与它们的当前最短距离,因为min=5,所以本来D[3]=8,现在v0->v4->v3=D[3]=min+2=7,本来的D[5]=11,现在的v0->v4->v5=D[5]=min+3=8,另外v0->v4->v6=D[6]=min+6=11,v0->v4->v7=D[7]=min+9=14。因此D数组的当前值为:{0,1,4,7,5,8,11,14,65535}。而原本的P[3]=1,此时P[3]=4,原本的P[5]=2,此时的P[5]=4,另外P[6]=4,P[7]=4,它表示v0到v3、v5、v6、v7点的最短路径它们的前驱是v4。此时P数组的值为:{0,0,1,4,2,4,4,4,0}

14.  之后的循环就是完全类似的了。得到最终结果

final数组{1,1,1,1,1,1,1,1,1},它表示所有顶点均完成了最短路径的查找工作。此时D数组为{0,1,4,7,5,8,10,12,16},它表示v0到各个顶点的最短路径数,

此时的P数组为{0,0,1,4,2,4,3,6,7}。比如P[8]=7它的意思是V0到V8的最短路径,顶点v8的前驱顶点是v7,再由P[7]=6,表示v7的前驱是v6,P[6]=3,前驱是v3。这样就可以得到,v0到v8的最短路径为v8<-v7<-v6<-v3<-v2<-v1<-v0即v0->v1->v2->v4->v3->v6->v7->v8。

其实最终返回的数组D和P,是可以得到V0到任意一个顶点的最短路径和路径长度的。例如v0到v8的最短路径并没有经过v5,但我们已经知道v0到v5的最短路径。D[5]=8,由P[5]可知道它的前驱顶点是v4。所以v0到v5的最短路径是v0->v1->v2->v4->v5。

迪杰斯特拉(Dijkstra)算法解决了从某个源点到其余顶点的最短路径问题。时间复杂度O(n2

现在如果我想求任决一点到其它点的最短路径。则还得有一个循环来处理复杂度就变成了O(n3)

那么我们再求一种求最短路径的算法如下

弗洛伊德(Floyd),它求所有顶点到所有顶点的时间复杂度也是O(n3),但其算法非常简洁优雅。

弗洛伊德(Floyd)算法

我们先来看一个简单的安全如下图的3个顶点的连通网图。

 

我们先定义两个二维数组D[3][3]和P[3][3],D代表顶点到顶点的最短路径权值

P代表对应顶点的最小路径前驱矩阵。

在未分析任何顶点前,我们将D命名为D-1其实它就是初始的图的邻接矩阵。将P命名为P-1,初始化为图中所示的矩阵

首先我们来分析,所有顶点经过v0后到达另一顶点的最短路径。因为只有三个顶点,因此需要查看v1->v0->v2,得到D-1[1][0]+D-1[0][2]=2+1=3。

D-1[1][2]表示的是v1->v2的权值为5,我们发现D-1[1][0]>D-1[1][0]+D-1[0][2]。通俗的说就是v1->v0->v2比直接v1->v2的距离还要近。所以我们就让D-1[1][2]=D-1[1][0]+D-1[0][2]=3,同样的D-1[2][1]也等于3。于是就有了D0的矩阵

因为有了变化,所以P矩阵对应的P-1[1][2]和P-1[2][1]也修改为当前中转的顶点v0的下标0,于是就有了P0看上图的转换过程

 

接下来有了D0和P0的基础上继续处理所有顶点经过v1和v2后到另一顶点的最短路径,得到D1和P1、D2和P2完成所有顶点 到所有顶点的最短路径计算工作

下面来个复杂的图的推演

 

代码如下

typedef   int   Pathmatirx[MAXVEX][MAXVEX];

typedef   int   ShortPathTable[MAXVEX][MAXVEX];

/*Floyd算法,求网图G中各顶点v到其余顶点w最短路径P[v][w]及带权长度D[v][w]*/

void   ShortestPath_Floyd(MGraph G,Pathmatirx *P,ShortPathTable  *D)

{

        int v,w,k;

        for(w=0;v<G.numVertexes;++v)

       {

             for(w=0;w<G.numVertexes;++w)

             {

                     (*D)[v][w] = G.matirx[v][w]; //初始化,即为对应点间的权值

                     (*P)[v][w] = w;   //初始化P

              }

        }

        for(k=0;k<G.numVertexes;++k)

        {

              for(v=0;v<G.numVertexes;++v)

              {

                   for(w=0;w<G.numVertexes;++w)

                    {

                            if((*D)[v][w]>(*D)[v][k]+(*D)[k][w])

                            {

                                  (*D)[v][w] = (*D)[v][k]+(*D)[k][w];

                                  (*P)[v][w]=(*P)[v][k];

                            }

                    }

               }

         }

}

 

1.    程序开始运行,第4~11行就是初始化D和P,使得它们成为下图的两个矩阵。从矩阵也得到v0->v1路权值是1,v0->v2路权值是5,v0->v3无边连线,所以路径权值为极大值65535。

2.    第12~25行,是算法的主循环,一共三个嵌套,k代表的就是中转顶点的下标。v代表起始顶点,w代表结束顶点。

3.    当K=0时,也就是所有的顶点都经过v0中转,计算是否有最短路径的变化。可惜结果是,没有任何变化,如下图

4.    当K=1时,也就是所有的顶点都经过v1中转。此时,当v=0,原本D[0][2]=5,现在由于D[0][1]+D[1][2]=4。因此由代码的第20行,二者取其最小值,得到D[0][2]=4,同理可得D[0][3]=8、D[0][4]=6,当v=2、3、4时,也修改一些数据。对权值进行修改也要对P[v][k]

至此,我们的最短路径就算是完成了,你可以看到矩阵第v0行的数值与迪杰斯特拉算法求得的D数组是完全相同的{0,1,4,7,5,8,10,12,16}

路径v0到v8   P[0][8]=1  先经过v1,然后将1取代0得到P[1][8]=2说明经过2,然后以2取代1 P[2][8]=4说明经过4,然后以4取代2 P[4][8]=3说明经过3…

最后昨到v0->v1->v2->v4->v3->v6->v7->v8


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Python学习笔记》是由皮大庆编写的一本关于Python语言学习的教材。在这本书中,作者详细介绍了Python语言的基础知识、语法规则以及常用的编程技巧。 首先,作者简要介绍了Python语言的特点和优势。他提到,Python是一种易于学习和使用的编程语言,受到了广大程序员的喜爱。Python具有简洁、清晰的语法结构,使得代码可读性极高,同时也提供了丰富的库和模块,能够快速实现各种功能。 接着,作者详细讲解了Python的基本语法。他从变量、数据类型、运算符等基础知识开始,逐步介绍了条件语句、循环控制、函数、模块等高级概念。同时,作者通过大量的示例代码和实践案例,帮助读者加深对Python编程的理解和应用。 在书中,作者还特别强调了编写规范和良好的编程习惯。他从命名规范、注释风格、代码缩进等方面指导读者如何写出清晰、可读性强的Python代码。作者认为,良好的编程习惯对于提高代码质量和提高工作效率非常重要。 此外,作者还介绍了Python的常用库和模块。他提到了一些常用的库,如Numpy、Pandas、Matplotlib等。这些库在数据处理、科学计算、可视化等领域有广泛的应用,帮助读者更好地解决实际问题。 总的来说,《Python学习笔记》是一本非常实用和全面的Python学习教材。通过学习这本书,读者可以系统地学习和掌握Python编程的基础知识和高级应用技巧,为以后的编程学习和工作打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值