最短路径

非网图的最小路径就是指两顶点之间经过的边数最小的路径;而对网图来说,最短路径,是指梁鼎点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点为源点,最后一个顶点为终点

下面讲解两种求最短路径的算法,分别为:迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd)算法,具体介绍如下:

迪杰斯特拉(Dijkstra)算法

这里写图片描述

迪杰斯特拉算法并不是一下求出 v0 v8 的最短路径,而是一步步求出它们之间顶点的最短路径,过程中基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到结果。具体代码如下所示:

#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 Dijkstra(MGraph G,int v0,Patharc *P,ShortPathTable *D){
   int v,w,k,min;
   int final[MAXVEX]; //final[i]=1表示求得顶点v0到vi的最短路径
   for(v=0; v<G.numVertexes; v++){  //初始化数据
      final[v]=0;   //全初始化为未知最短路径状态
      (*D)[v]=G.arc[v0][v];  //将与v0点有连线的顶点加上权值
      (*P)[v]=0;  //初始化路径数组
   }
   (*D)[v0]=0;  //v0到v0的路径为0
   final[v0]=1;  //v0到v0不需要求路径
   /*开始主循环,每次求得v0到某个v顶点的最短路径*/
  for(v=1; v<G.numVertexes; v++){
      min = INFINITY;  //当前所知的离v0顶点的最近距离
      for(w=0; w<G.numVertexes ; w++){//寻找离v0最近的顶点
         if((!final[w]) && (*D)[w]< min){ //final为0且发现有更小的值
            k=w;
            min=(*D)[w];
        }
     }
     final[k]=1;  //将目前找到的最近顶点置为1
     for(w=0; w<G.numVertexes ; w++){  //修正当前最短路径及距离,修正其他结点和v0的最小距离。
        if((!final[w]) && (min+G.arc[k][w])<(*D)[w]){
           (*D)[w]=min+G.arc[k][w];
           (*P)[w]=k;
        }
    }
  }
}

详解:准备好上图对应的邻接矩阵, v0 v0 自身,权值和结果均为0,D数组为{0,1,5, , , , , , }, v0 算是求到最短路径,所以final[0]=1,此时final数组为{1,0,0,0,0,0,0,0,0},P={0,0,0,0,0,0,0,0,0},初始化工作完成。

在D数组中,D数组中final非1的最小值为1,比较 v0 与其他顶点的边得到最近顶点,所以k=1 ,D={0,1,4,8,6, , , , },final={1,1,0,0,0,0,0,0,0},P={0,0,1,1,1,0,0,0,0}。

在D数组中,D数组中final非1的最小值为4,比较 v1 与其他顶点的边得到最近顶点,所以k=2,D={0,1,4,8,5,11, , , },final={1,1,1,0,0,0,0,0,0},P={0,0,1,1,2,2,0,0,0}。

在D数组中,D数组中final非1的最小值为5,比较 v2 与其他顶点的边得到最近顶点,所以k=4,D={0,1,4,7,5,8,11,14, },final={1,1,1,0,1,0,0,0,0},P={0,0,1,4,2,4,4,4,0}。

在D数组中,D数组中final非1的最小值为7,比较 v4 与其他顶点的边得到最近顶点,所以k=3 ,D={0,1,4,7,5,8,10,14, },final={1,1,1,1,1,0,0,0,0},P={0,0,1,4,2,4,3,4,0}。

在D数组中,D数组中final非1的最小值为10,比较 v3 与其他顶点的边得到最近顶点,所以k=6,D={0,1,4,7,5,8,10,12,17},final={1,1,1,1,1,0,1,0,0},P={0,0,1,4,2,4,3,6,6}。

在D数组中,D数组中final非1的最小值为12,比较 v6 与其他顶点的边得到最近顶点,所以k=7,D={0,1,4,7,5,8,10,12,16},final={1,1,1,1,1,0,1,1,0},P={0,0,1,4,2,4,3,6,7}。

列表内容

弗洛伊德(Floyd)算法

这里写图片描述

先定义两个二维数组D[n][n]和P[n][n],D代表顶点到顶点的最短路径权值,P代表对应顶点的最小路径的前驱矩阵。在未分析任何顶点前,D命名为 D1 ,实质上为初始的图的邻接矩阵,P命名为 P1

D0[v][w]=minD1[v][w]D1[v][0]+D1[0][w]

具体代码如下所示:

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(v=0; v<G.numVertexes; ++v){  //初始化数据
      for(w=0; w<G.numVertexes; ++w){
         (*D)[v][w]=G.matirx[v][w];  //(*D)[v][w]表示对应点间的权值
         (*P)[v][w]=w;  //初始化P
      }
   }

  for(k=0; k<G.numVertexes; ++k){  //三层嵌套  O(n^3)
      for(v=0; v<G.numVertexes ; ++v){
         for(w=0; w<G.numVertexes ; ++w){
            if((*D)[v][w] > ((*D)[v][k]+(*D)[k][w])){
               /*如果经过下标为k顶点路径比原来两点间路径更短,将当前两点间权值设为更小的一个*/
               (*D)[v][w]=(*D)[v][k]+(*D)[k][w]);
               (*P)[v][w]=(*P)[v][k];  //路径设置经过下标为k的顶点
            }
        }
     }

  }
}

这里写图片描述

详解:初始化上图的邻接矩阵D和矩阵P, 当K=0时,即所有顶点都经过 v0 ,计算是否有最短路径的变化,最后发现没有任何变化。

这里写图片描述

当K=1时,即所有顶点都经过 v1 ,计算是否有最短路径的变化,当v=0时,原本D[0][2]=5,现在由于D[0][1]+D[1][2]=4,同理D[0][3]=8,D[0][4]=6在路径矩阵P上也要做处理,将其改为当前的P[v][k]。

这里写图片描述

当K=2时一直到8结束,表示针对每个顶点做中转得到的结果, D0 是以 D1 为基础, D1 是以 D0 为基础,……, D8 是以 D7 为基础,相互联系,路径矩阵P也是如此。

这里写图片描述

求最短路径的显示代码为:

for(v=0; v<G.numVertexes; ++v){
   for(w=v+1; w<G.numVertexes; w++){
      printf("v%d-v%d weight:%d",v,w,D[v][w]);
      k=P[v][w];
      printf("path:%d",v);
      while(k!=w){
         printf("-> %d",k);
         k=P[k][w];
      }
      printf("-> %d\n",w);
   }
   printf("\n");
}

迪杰斯特拉算法的时间复杂度为 O(n2) ,弗洛伊德算法的时间复杂度为 O(n3) ,若面临要求所有顶点至所有顶点的最短路径问题时,弗洛伊德(Floyd)算法是个不错的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值