图论最短路径

本文介绍了几种图论最短路径算法。Floyed算法可找所有两点间最短距离,适合处理不确定起点、数据量小的问题;Dijkstra算法解决已知起点到终点的最短距离;Bellman - Ford算法能解决有负边权问题;SPFA算法是Bellman - Ford算法的队列实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Floyed(弗洛伊德)算法:

点u、v如果有边相连,则dis[u][v]=w[u][v]。w表示u-v点的距离

如果不相连则dis[u][v]=0x7fffffff,表示为无穷

For (k = 1; k <= n; k++)  

  For (i = 1; i <= n; i++)  

    For (j = 1; j <= n; j++)   

      If (dis[i][j] >dis[i][k] + dis[k][j])   

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

这是算法的核心代码,通过三重循环可以找到两点之间的最短距离,注意,是所有两个点之间的最短距离,但是时间复杂度有点高,是n的三次方。适合处理不确定起点,数据量较小的问题。

变形:

For (k = 1; k <= n; k++)

  For (i = 1; i <= n; i++)

    For (j = 1; j <= n; j++)

  dis[i][j] = dis[i][j] || (dis[i][k] && dis[k][j]);

因为Floyed算法找到了所有的两点之间最短距离,故在没有边权的情况下,可以使边权是bool数,判断其是否相连。

 

dijkstra迪杰斯特拉算法:(从我以前的博客复制粘贴的,补充图论的最短路径的方法):

迪杰斯特拉算法不同于Floyed算法,迪杰斯特拉算法趋向于解决知道起点的问题,故不需要找到所有两点之间的最短距离,只需要找到起点距离终点的最短距离,故时间复杂度仅有n方。

迪杰斯特拉算法是一个最短路径的方法,在这里简称dij,它在确立出发点的基础上,设出发点是start,你想到达的点是end,其中一共有n个点,我们可以先找一步到达的距离,然后与分两步到达的距离比较,取最小值,然后让得到的结果与三步到达的距离比较,取最小值,最终让得的的最小值与n-1部到达的距离比较,取最小值,这样的最小值就是我们要得到的距离最少的最优点了。

但dij在c++中实现可不是那么好实现的,我们要把点之间的距离全部得到,在刚开始的几部中如果你要求start到end之间的距离,比如1,2,3点,1-2之间距离是1,2-3之间距离是1,1-3之间距离是1,这样1-3的距离最短就是1,为了避免类似1-2-3这样的情况,就是我上面一段说的那些了,我们要分步的比较,在c++中我们一般会用这样的方法:min是否大于1-3+3-4这样的形式,而1-3本身可能并不是最小值,因此我们在计算过程中不只把start到end的最小值算了出来,我们还把start到所有点的最小值算了出来。下面介绍一下dij在c++中实现的函数:

 

int i,j,k,min,tmp;

void dij(int start,int dist【】,flag【】) //dist【i】意是点start到点i的距离,flag【i】=0意思是点start到i的距离最小值未得到,=1意思是已经得到最短距离。我们确立flag的目的是防止重复与过多的循环。

{

for(i=1;i<=n;++i)     //详细的没有指出,n是指全部的点

{flag[i]=0;

dist[i]=long[1][i]     //long表示1-i之间的距离   }

flag【start】=1;  //出发点与出发点的最短距离,默认为已知

dist【start】=0;  //出发点与出发点的距离0;

//这之后就是在从start1步到达end和n-1步到达end的最短距离的比较选择,也是dij中的核心

for(i=1;i<n;++i){

min=inf;   //inf是一个极大值,表示无穷大的数。

for(j=1;j<=n;++j) if(flag[j]==0&&dist[j]<min)  {min=dist[j]; k=j;}

flag[k]=1;  //因为一步里面到达的它是距离最少的,所以start到k点距离最小。

//下面的循环就是求第二步到第n-1步最小值的。不太好掌握

for(j=1;j<=n;++j){ tmp= long[k][j]==INF ? INF : (min + mMatrix[k][j]));  if(flag[j]==0&&dist[j]>temp) dist【j】=tmp;}//不仅如此,在这一步中我们得到了每一个分两步到达各点的距离的最小值,然后返回上一个循环找到两步到达的最小值的点,然后再进行这一个循环得到分三步到达的点的距离的最小值,以此类推。

然后我们想要到哪个点,直接用dist【i】就是最小距离点了。

}

 

 

Bellman-Ford算法O(NE):

这是一种可以解决有负边权的算法,但是不能够解决负边权回路的问题(至于为什么之后再补)。同迪杰斯特拉算法相似,这个算法有起点,是找起点离所有点的最短距离。

dis[s]=0,dis[v]=∞(v≠s),pre[s]=0;//s是起始点无穷可以是0x7fffffff

实现原理:

For (i = 1; i <= n-1; i++)

For (j = 1; j <= E; j++)         //枚举所有边,注意是边!!! 

 if (dis[u]+w[j]<dis[v])          //u、v是这条边连接的两个点。  

{        dis[v] =dis[u] + w[j];      //求距离  

pre[v] = u;   }  //pre在这里表示的是v点的前面走的是u

 

SPFA算法O(kE):

这个算法是Bellman-Ford算法的队列实现。

开始时把起点放入队列,之后从队列取出一个元素并把它相邻的点修改,若相邻的点修改成功则入队,直到队列为空。其形式上与广度优先搜索非常相似,但是其队列中取出的元素还可以返回,这是其一大特点。

算法实现:

 dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。     team[1..n]为队列,头指针head,尾指针tail。

布尔数组exist[1..n]记录一个点是否现在存在在队列中。    

初始化:dis[s]=0,dis[v]=∞(v≠s),//依然无穷可以是0x7fffffff

memset(exist,false,sizeof(exist));

 team[1]=s; head=0; tail=1;exist[s]=true;   //将第一个点入队

do     {if (dis[v]>dis[u]+w[u][v])             //头指针要指向当前点的前驱方向,既u

  { dis[v]=dis[u]+w[u][v];

          pre[v]=u;

          if (!exist[v]) //如果未false,说明已经被取出 

{//否则指针指向下一个,v入队            

exist[v]=true;            }       }     }    

while (head < tail);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值