测试

7.6 图的最短路径

在一个赋权有向图G={V,E}中,每一条边都有一个权值W(Weight)(该权值通常称之为成本或距离),求G图中某一个顶点v1到其他顶点具有最小权值总和的路径,这类问题就称为最短路径问题(TheShortest Path Problem)。

本节将探讨单点对全部顶点的最短路径及所有顶点之间的最短路径。

7.6.1 单点对全部顶点的最短路径

问题的提法是:给定一个有向带权图G={V,E}与求一个固定顶点v1(即源点),要求找出从v1到G中其他各顶点的最短路径。这类问题称为单源最短路径问题。

为了求得这些最短路径,狭克斯特拉(Dijkstra)提出了按权值的递增次序,逐步产生最短路径的算法。首先求出权值最小的一条最短路径,然后参照它求出权值次小的一条最短路径,依次类推,直到从顶点v到其他各顶点的最短路径全部求出为止。

【范例6】考虑下图所示的有向带权图,边上的数字为该边的权值(成本或距离),设源点v1=6,给出它到各个顶点的最短路径。

初始状态时,被选顶点集合S={6},最短距离集合D={0}。

第1步、D[1]=∞,D[2]=12,D[3]=∞,D[4]=20,D[5]=14,D[2]=12最小

被选顶点集合S={6,2}。

最短距离集合D={0,12}。

2、D[1]=∞,D[2]=12,D[3]=12+6=18,D[4]=20,D[5]=14,D[5]=14最小

被选顶点集合S={6,2,5}。

最短距离集合D={0,12,14}。

3、D[1]=26,D[2]=12,D[3] =12+6=18,D[4]=20,D[5]=14,D[3]=18最小

被选顶点集合S={6,2,5,3}。

最短距离集合D={0,12,14,18}。

4、D[1]=26,D[2]=12,D[3]=18,D[4]=20,D[5]=14。D[4]=20最小

被选顶点集合S={6,2,5,3,4}。

最短距离集合D={0,12,14,18,20}。

5、D[1]=26,D[2]=12,D[3]=18,D[4]=20,D[5]=14。D[1]=26最小

被选顶点集合S={6,2,5,3,4,1}。

最短距离集合D={0,12,14,18,20,26}。

因此,由顶点6到其他各顶点的最短路径如下:

l  顶点6→顶点1,距离26

l  顶点6→顶点2,距离12

l  顶点6→顶点3,距离18

l  顶点6→顶点4,距离20

l  顶点6→顶点5,距离14

 

下面讨论Dijkstra算法的具体实现:

设集合S存放已经求出的最短路径的终点。初始状态时,集合S中只有一个源点,不妨设为v1。以后每求得一条最短路径(v1,…,vk),就将vk加入到集合S中,直到全部顶点都加入到集合S中,算法就可以结束了。

为了找到从源点v1到其他顶点的最短路径长度,引入一个辅助数组dist[]。它的每一个分量dist[i]表示当前找到的从源点v1终点vi的最短路径的长度。它的初始状态是:若从源点v1到顶点vi有边,则dist[i]为该边上的权值;若从源点v1到顶点vi没有边,则dist[i]的值为INFINITE。

设第一条最短路径为(v1,vk),其中k满足:

dist[k]=min{ dist[i] | vi∈V-{v1},V为图的顶点集合}

假设下一条最短路径的终点为vj,则最短路径或为(v1,vj),或为(v1,vk,vj),其长度或者是边(v1,vj)的权值,或者是dist[k]+边( vk,vj )的权值。

一般情况下,下一条最短路径总是在“由已产生的最短路径再扩充一条边”形成的最短路径中得到。假设S是已求得的最短路径的终点的集合,则可以证明:下一条最短路径必然是从v1出发,中间经过S中的顶点再扩充一条边便可到达顶点vi∈V-S的各条路径中的最短者。即若设下一条最短路径(v1,…,vk)的终点为vk,则有

dist[k]=min{ dist[i] | vi∈V-S }

反证法:设在路径(v1,…,vk)上存在另一个顶点vp∈V-S,使得(v1,…,vp,vk)成为另一条终点不在S,而长度比路径(v1,…,vk)还短的路径。

然而,这个假设不成立。因为我们是按照最短路径的长度递增次序,来逐次产生各条最短路径,因此,长度比这条路径短的所有路径均已产生,而且它们的终点也一定已在集合S中,故假设不成立。

在每次求得一条最短路径之后,将其终点vk加入集合S,然后对所有的vi∈V-S,修改其dist[i],使 dist[i]=min{ dist[i],dist[k] + 边(vk,vi )的权值 }。

 

【范例程序】用狭克斯特拉(dijksta)算法求单点对全部顶点的最短距离。

/*

[名称]:ch07_08.cpp

[示范]:Dijkstra算法(单源点对全部顶点的最短距离)

*/

#include<iostream>

using namespace std;

#define N 7 //顶点数+1

#define INFINITE 99999//无穷大

intEdgeData[9][3]={{1,2,8},{2,3,6},{2,5,16},{3,4,10},{3,6,18},

{5,1,12},{6,2,12},{6,4,20},{6,5,14}};//有向图的边集合

int S[N];//最短路径的顶点集合

int V[N];//图的顶点集合

 

//邻接矩阵初始化

void InitialMatrix(intAdjacencyMatrix[N][N])

{ for(int i=1;i<N;i++)

     for(int j=1;j<N;j++)

         {  if(i==j)

                AdjacencyMatrix[i][j]=0;//主对角线元素均为0

             else

                       AdjacencyMatrix[i][j]=INFINITE;//其余元素均置为无穷大

         }

}

//生成邻接矩阵

void CreateMatrix(intAdjacencyMatrix[N][N],int EdgeNum)

{ //据边集合EdgeData改写邻接矩阵 

       inti,VertexI,VertexJ;

   for(i=0;i<EdgeNum;i++)

       {  VertexI=EdgeData[i][0];//起点编号

          VertexJ=EdgeData[i][1];//终点编号

         AdjacencyMatrix[VertexI][VertexJ]=EdgeData[i][2];//成本

       }

}

//输出邻接矩阵

void DisplayMatrix(intAdjacencyMatrix[N][N])

{    int i,j;

       //输出表头

       cout<<endl<<"有向图的邻接矩阵"<<endl;

       cout<<"顶点\t";

       for(i=1;i<N;i++)

         cout<<"vex"<<i<<"\t";

       cout<<endl;

       //输出邻接矩阵

       for(i=1;i<N;i++)

       {  cout<<"vex"<<i<<"\t";

              for(j=1;j<N;j++)

              {     if(AdjacencyMatrix[i][j]==INFINITE)

                            cout<<"x"<<"\t";//无穷大用x替代

                     else

                            cout<<AdjacencyMatrix[i][j]<<"\t";

              }

              cout<<endl;

       }

       cout<<endl;

}

//查找最小路径长度

void FindMinEdge(int dist[N],int &x,int&y)

{//x终点编号,y最小路径长度

       inti,v;

       intmin=INFINITE;

       for(i=1;i<N;i++)

       {//在剩余顶点集找具有最小路径长度的顶点

              if(V[i]==1 && dist[i]>0&& dist[i]<min)

              {  v=i;

                     min=dist[i];

              }

       }

       x=v;

       y=min;

}

//改变最短路径距离

void ChangeDist(int v,int dist[N],intAdjacencyMatrix[N][N])

{     inti,w;

       for(i=1;i<N;i++)

       {     if(V[i]==1)//在剩余顶点集改变最小路径长度

              {     w=AdjacencyMatrix[v][i];//获取与顶点v相关边的权值

                     if(w<INFINITE&& dist[i]>0 && dist[v]+w<dist[i])

                            dist[i]=dist[v]+w;

              }

       }

}

//狭克斯特拉算法(求单源点对全部顶点的最短路径)

void Dijkstra(int vertex,int dist[N],intAdjacencyMatrix[N][N])

{  int D[N];//纪录最小路径长度

       inti,x,y;

       S[1]=vertex;//源点进入最小路径顶点集

       V[vertex]=0;//标识源点在原顶点集中被剔除

       D[1]=0;//源点到源点的距离置0

       for(i=1;i<N-1;i++)

       {  FindMinEdge(dist,x,y);

      S[i+1]=x;//具有最小路径长度的终点进入

          V[x]=0;//从原顶点集中剔除

      D[i+1]=y;//纪录最小路径长度

          ChangeDist(x,dist,AdjacencyMatrix);

       }

       //输出

       cout<<"源点对全部顶点的最短路径长度"<<endl;

       for(i=1;i<N;i++)

          cout<<"顶点"<<vertex<<"=>"<<S[i]<<": 距离"<<D[i]<<endl;

       cout<<endl;

}

//主函数

void main()

{     inti,dist[N];

       intAdjacencyMatrix[N][N];//定义邻接矩阵

   int vertex=6;//单源点编号

   for(i=0;i<N;i++)

       {     S[i]=0;//设图的顶点均不在最小路径集合中

              V[i]=1;//设图的顶点均在图的顶点集中

       }

       InitialMatrix(AdjacencyMatrix);//邻接矩阵初始化

    CreateMatrix(AdjacencyMatrix,9);//据边集合创建邻接矩阵

   DisplayMatrix(AdjacencyMatrix);//输出邻接矩阵

   //dist[]初始化

   for(i=1;i<N;i++)

              dist[i]=AdjacencyMatrix[vertex][i];

       Dijkstra(vertex,dist,AdjacencyMatrix);//狭克斯特拉算法

       system("pause");

}

(该程序存在着一个缺点,它没有给出最短路径,仅给出最短路径的距离。作为练习,请为该程序配置一个纪录最短路径的函数)

7.6.2顶点之间的最短路径

由于Dijkstra方法只能求出某一点到其他顶点的最短路径,如果要求出图中任意两点甚至所有顶点间的最短路径,则必须使用弗洛伊德(Floyd)算法。

设有向图的顶点数为,Floyd算法如下:

1、,这里,为有向边的成本,若无边则用(无穷大)表示。

2、

其中,表示从顶点经过顶点到达顶点的最短路径。

3、代表到的最短距离,矩阵是所要求的最短路径成本矩阵。

例如,用弗罗伊德(Floyd)算法求下图中各顶点间的最短路径的步骤如下:

1、求,为有向边的成本,若无边则用(无穷大)表示。

2、求,即求由顶点经过顶点1到达顶点的最短路径。

依序求得其他各顶点的值可得到矩阵。

3、求,即求由顶点经过顶点2到达顶点的最短路径。

依序求得其他各顶点的值可得到矩阵。

4、求出,即求由顶点经过顶点3到达顶点的最短路径。

依序求得其他各顶点的值可得到矩阵。

由上例可知,如果一个赋权有向图有个顶点,我们只有生成了该图的邻接矩阵,对它执行次循环与相应操作,便可逐一产生,而就是我们的所求。

【范例程序】用弗洛伊德(Floyd)算法求所有顶点两两之间的最短距离。

/*

[名称]:ch07_09.cpp

[示范]:Floyd算法(所有顶点两两之间的最短距离)

*/

#include<iostream>

using namespace std;

#define N 4 //顶点数+1

#define INFINITE 99999//无穷大

//有向图的边集合

intEdgeData[5][3]={{1,2,4},{2,1,6},{2,3,2},{1,3,11},{3,1,3}};

//邻接矩阵初始化

void InitialMatrix(intAdjacencyMatrix[N][N])

{ for(int i=1;i<N;i++)

     for(int j=1;j<N;j++)

         {  if(i==j)

                AdjacencyMatrix[i][j]=0;//主对角线元素均为0

             else

                       AdjacencyMatrix[i][j]=INFINITE;//其余元素均置为无穷大

         }

}

//生成邻接矩阵

void CreateMatrix(intAdjacencyMatrix[N][N],int EdgeNum)

{ //据边集合EdgeData改写邻接矩阵 

       inti,VertexI,VertexJ;

   for(i=0;i<EdgeNum;i++)

       {  VertexI=EdgeData[i][0];//起点编号

          VertexJ=EdgeData[i][1];//终点编号

         AdjacencyMatrix[VertexI][VertexJ]=EdgeData[i][2];//成本

       }

}

//输出

void DisplayMatrix(intAdjacencyMatrix[N][N])

{    int i,j;

       cout<<"顶点\t";

       for(i=1;i<N;i++)

         cout<<"vex"<<i<<"\t";

       cout<<endl;

       for(i=1;i<N;i++)

       {  cout<<"vex"<<i<<"\t";

              for(j=1;j<N;j++)

              {     if(AdjacencyMatrix[i][j]==INFINITE)

                            cout<<"x"<<"\t";//无穷大用x替代

                     else

                            cout<<AdjacencyMatrix[i][j]<<"\t";

              }

              cout<<endl;

       }

       cout<<endl;

}

//Floyd算法

void Floyd(int AdjacencyMatrix[N][N])

{     inti,j,k;

       for(k=1;k<N;k++)

              for(i=1;i<N;i++)

                     for(j=1;j<N;j++)

                            if(AdjacencyMatrix[i][k]+AdjacencyMatrix[k][j]<AdjacencyMatrix[i][j])

                              AdjacencyMatrix[i][j]=AdjacencyMatrix[i][k]+AdjacencyMatrix[k][j];

}

//主函数

void main()

{     intAdjacencyMatrix[N][N];//定义邻接矩阵

   InitialMatrix(AdjacencyMatrix);//邻接矩阵初始化

   CreateMatrix(AdjacencyMatrix,5);//据边集合创建邻接矩阵

   cout<<"有向图的邻接矩阵"<<endl;

       DisplayMatrix(AdjacencyMatrix);//输出邻接矩阵

       cout<<"所有顶点两两之间的最短距离"<<endl;

       Floyd(AdjacencyMatrix);//弗洛伊德算法

       DisplayMatrix(AdjacencyMatrix);

   system("pause");

}

因Floyd算法较为复杂,也可以用Dijkstra算法,依序以各顶点为起始顶点,如此一来可以得到相同的结果,试对此进行验证。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值