最短路径算法

目录

一、BFS求无权图的单源点最短路径

二、迪杰斯特拉,带权图的最短路径

三、弗洛伊德(Floyd)算法

小结:

下一篇:有向无环图DAG


单源最短路径和每对顶点间的最短路径

 

 

一、BFS求无权图的单源点最短路径

原理:从源点开始BFS遍历,按BFS的规则,将每个被遍历到的顶点(且没被访问过)的距离设为上一个顶点的距离值+1

代码(伪):

//求u到其他顶点的最短路径,G为无权图

void BFS_MIN_Distance(Graph G,int u){

//数组d代表从u到i的最短路径,初始化为无限大

//path代表是从哪个顶点过来的,初始化为-1

for(i=0;i<G.vexnum;i++){

d[i]=inf;

path[i]=-1;

}

//从u开始当然u到u距离是0

d[u]=0;

//标记数组

visited[u]=TRUE;

//u入队

EnQueue(Q,u);

while(!isEmpty(Q)){

DeQueue(Q,u);//队头元素出队

//循环遍历顶点u的下一个相邻顶点

for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){

if(!visited[w]){

//若没有被访问过,则把源点到上一个顶点的距离+1给当前顶点

d[w]=d[u]+1;

path[w]=u;

visited[w]=TRUE;

EnQueue(Q,w)''

}

}

}

}

 

也可以使用广度优先生成树,来看该顶点在第几层,距离就是几

 

 

二、迪杰斯特拉,带权图的最短路径

源点为v0的图

使用3个数组

先处理源点,将final表中v0设为最短,dist[v0]=0,path中将v0相邻的顶点设为0,表示他们的前驱顶点为v0

按以下规则执行

1、从dist表中距离最短的且没有被找到最短路径的顶点(final中为false的顶点)

2、从该顶点出发更新(计算其相邻顶点的当前最短路径即通过比较新旧距离大小)其相邻顶点(相邻顶点中final为false的)的距离和path表中的前驱顶点,最后将final表中该顶点改为已找到最短路径

3、重复1、2直到final表中全为已找到

例:处理v0后的第一个顶点v4

经过1、2后

接下来肯定是操作V3这里不再赘述

 

通过path数组找到路径

int v=path[v];

v_path[vexnum];

int count=0;

while(path(v)!=-1)

{

v_path[count]=v;

v=path[v];

count++;

}

 

时间复杂度分析

每次都要扫描找到最小的distance值,进行n-1轮

无论是邻接矩阵还是邻接表都需要O(n^2)或O(|V|^2)

 

对于负权值带权图

迪杰斯特拉算法就无法作用了

例如:

解决方案,将所有权值做一个平移,使得所有权值都是正数

 

负权值应用举例:在计算某些可以补充或‘节省’的值时,比如骑电动车(初始12格电)从A到B要消耗5格电剩余7格,但是从A到C再到B,虽然到C要消耗6格电但是可以充到15格电,计算得出A->C->B最后电动车剩余10格电。

 

 

 

 

三、弗洛伊德(Floyd)算法

动态规划

对于该矩阵创建两个二维数组

A(-1)(各个顶点间的最短路径长度)和path(-1)(两个顶点之间的中转点)

#0(阶段0):允许在V0中中转,求最短路径?——求A(0)和path(0)

通过如下规则:

对阶段k

若A(k-1)也即上一个A中两个顶点间的最短距离大于当前通过V0中转到达的最短距离(A(k-1)[i][j]>A(k-1)[i][k]+A(k-1)[k][j]),则更新两顶点间的最短路径距离(A(k)[i][j]=>A(k-1)[i][k]+A(k-1)[k][j]),将中转矩阵中两个顶点的中转顶点标记为中转顶点Vk,每个阶段都扫描全部的元素看是否满足上面的关系并更新

例如阶段0

经过n(顶点个数)轮递推后得到A(n-1)和path(n-1)

最终

就是通过前一个矩阵的状态推出下一个矩阵的状态

 

由于这个例子过于简单,引起了我更多的思考,若有两个以上的中转点,这个算法会怎样处理?

例如:

边(顶点->另一个顶点)

权值

0->1

5

1->2

5

2->3

5

显然初始时1->2为无穷,1->3也是无穷,要使得能1->3得通过1与2进行中转

则通过阶段0,顶点0作为中转点无法更新任何路径

阶段1,通过顶点1中转,可更新0->1->2(即0->2) 为10替换掉原来的∞

阶段2,通过顶点2中转,可更新0->1->2->3(即0->3)为15替换掉原来的∞

阶段3,显然无法做任何更新

至此floyd算法完成执行

 

若要得到顶点0到3中所有经过的路径,可以查path矩阵

首先可查0->3通过2中转,再查0->2通过1中转,再查0->1没有中转即path表中的-1,即得出所有路径,对于更复杂的例子可以使用递归来实现

例如:

v0->v2->v1->v3->v4

若根据path可得v0-->v3>v4

分别将v0->v3和v3->v4展开

得到v0->v2,v2->v3,v3->v4

继续展开得:v0->v2,v2->v1,v1->v3,v3->v4

最终得到结果(即对每对顶点都进行展开)

 

 

 

代码实现:

//Floyd算法伪代码实现

//初始化部分根据图的信息初始化A和path矩阵...

 for(int k=0;k<n;k++){//k个阶段,选第k个顶点作为中转顶点

         for(int i=0;i<0;i++){//遍历矩阵中每一个元素,需要双重循环

for(int j=0;j<n;j++){//注:A[i][j]代表顶点

if(A[i][j]>A[i][k]+A[k][j]){//如果通过k中转的路径更短则,ij的距离更新为新距离

A[i][j]=A[i][k]+A[k][j];//更短的路径长度

path[i][j]=k;//中转点

}

}

 }

时间复杂度为O(|V|^3)

空间复杂度为O(|V|^2)

 

 

Floyd可以解决带负权值的图

但解决不了带负权回路的图(可能没有最短路径的图)

不难发现每经过一次回路,路径长度就会-2

 

 

小结:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值