最短路问题 小结


分类: 小结   93人阅读  评论(1)  收藏  举报
最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。


确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。


确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。


全局最短路径问题 -  求图中所有的最短路径。




最常用的路径算法有:

1.Floyd算法。

2.Dijkstra算法。

3.Bellman-ford算法。

4.SPFA算法。


下面逐个来讲解。。。

1.Floyd算法。
求多源、无负权边的最短路。时效性较差,时间复杂度O(V^3)。空间复杂度为O(N^2)。是解决任意两点间的最短路径的一种算法。

核心代码:


[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:14px;">void floyd(int n)  
  2. {  
  3.     int i,k,j;  
  4.     for(k=0; k<n; k++)//遍历所有的中间点  
  5.         for(i=0; i<n; i++)//遍历所有的起点  
  6.             for(j=0; j<n; j++)//遍历所有的终点  
  7.                 if(map[i][j]>map[i][k]+map[k][j])//如果当前i-->j的距离大于i-->k--->j的距离之和  
  8.                     map[i][j]=map[i][k]+map[k][j];//更新从i--->j的最短路径  
  9. }</span>  




2.Dijkstra算法
求单源、无负权的最短路。时效性较好,时间复杂度为O(V*V+E);
以贪心法选取未被处理的具有最小权值的节点,然后对其的出边进行松弛操作;



核心代码:


[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int dijkstra (int v)  
  2. {  
  3.     //初始化  
  4.     for (i=1;i<=n;i++)  
  5.     {  
  6.         vist[i]=0;  
  7.         dis[i]=map[v][i];  
  8.     }  
  9.     //进行n-1次操作  选择最短路  
  10.     for (i=1;i<n;i++)  
  11.     {  
  12.         mn=inf;  
  13.         //与当前点直接相连的点的最短路  
  14.         for (j=1;j<=n;j++)  
  15.         {  
  16.             if (!vist[j] && dis[j]<mn)  
  17.             {  
  18.                 mn=dis[j];  
  19.                 pos=j;  
  20.             }  
  21.         }  
  22.         vist[pos]=1;  
  23.         //松弛操作  
  24.         for (j=1;j<=n;j++)  
  25.         {  
  26.             if (!vist[j] && dis[j]>dis[pos]+map[pos][j])  
  27.                 dis[j]=dis[pos]+map[pos][j];  
  28.         }  
  29.     }  
  30.     return dis[n];  
  31. }  



3.Bellman-ford算法
求单源最短路,可以判断有无负权回路(若有,则不存在最短路),时效性较好,时间复杂度O(VE)。和dijkstra一样都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确
的值替代,直至得到最优解。
它的原理是对图进行n-1次松弛操作,得到所有可能的最短路径。其优于dijkstra的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高。但是可以进行优化,提高效率。



负权环判定
因为负权环可以无限制的降低总花费,所以如果发现第n次操作仍可降低花销,
就一定存在负权环。



核心代码:


[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <pre name="code" class="html">struct node  
  2. {  
  3.     int u;//起点  
  4.     int v;//终点  
  5.     int w;//权值  
  6. } p[inf];  
  7.   
  8. int dis[inf];//用来保存远点至当前点的最短路径    迭代更新  
  9. //   点数    边数    源点  
  10. int nodenum,edgenum,source,i,j;  
  11.   
  12. //初始化  
  13. void init ()  
  14. {  
  15.   
  16.     scanf ("%d%d%d",&nodenum,&edgenum,&source);  
  17.     for (i=0; i<=nodenum; i++)  
  18.     {  
  19.         dis[i]=inf;  
  20.     }  
  21.     dis[source]=0;  
  22.     for (i=0; i<edgenum; i++)  
  23.     {  
  24.         scanf ("%d%d%d",&p[i].u,&p[i].v,&p[i].w);  
  25.         if (dis[p[i].u]==source)  
  26.             dis[p[i].v]=p[i].w;  
  27.     }  
  28. }  
  29.   
  30. int Bellman_ford ()  
  31. {  
  32.     int flag=0;  
  33.     for (i=0; i<nodenum-1; i++) //n-1次循环找最短路  
  34.     {  
  35.         flag=0;  
  36.         for (j=0; j<edgenum; j++)  
  37.         {  
  38.             if (dis[p[j].v] > dis[p[j].u] + p[j].w)//松弛操作   更新dis数组  
  39.             {  
  40.                 dis[p[j].v] = dis[p[j].u] + p[j].w;  
  41.                 flag=1;  
  42.             }  
  43.         }  
  44.         if (flag==0)  
  45.             break;  
  46.     }  
  47.     //判断负环  
  48.     //上面两层for循环结束之后  dis数组里保存的就是远点到当前点的最短路  
  49.     //如果还能进行松弛操作的话就说明有负环存在  
  50.     for (i=0; i<edgenum; i++)  
  51.     {  
  52.         if (dis[p[i].v] > dis[p[i].u] + p[i].w)  
  53.         {  
  54.              
  55.             return ;//存在负环 返回0 还是 1 根据题意而定  
  56.         }  
  57.     }  
  58.   
  59. }  


 
 

4.SPFA算法是Bellman-Ford的队列优化,时效性相对好,时间复杂度O(kE)。(k<<V)。SPFA算法采用一系列的松弛操作以得到从某一个节点出发到达图中其它所有节点的最短路径。所不同的是,SPFA算法通过维护一个队列,使得一个节点的当前最短路径被更新之后没有必要立刻去更新其他的节点,从而大大减少了重复的操作次数。

核心代码:

[html]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. void SPFA(int s,int e)  // s点 到 e点  
  2. {  
  3.     int l,r,i;  
  4.     l=r=0;  
  5.     memset(vis,0,sizeof(vis));  
  6.     memset (dis,inf,sizeof (dis));  
  7.     dis[s]=0;  
  8.     q[r++]=s;//进队列  
  9.     vis[s]=1;//标记 进队列1  
  10.     //不在队列为0  
  11.     while(l<r)  
  12.     {  
  13.         int p=q[l++];//出队列  
  14.         for(i=0; i<n; i++)  
  15.         {  
  16.             if(dis[i] > dis[p] + map[p][i])  
  17.             {  
  18.                 dis[i] = dis[p] + map[p][i];  
  19.                 if(vis[i]==0)  
  20.                 {  
  21.                     q[r++] = i;  
  22.                     vis[i] = 1;  
  23.                 }  
  24.             }  
  25.         }  
  26.         vis[p] = 0;  
  27.     }  
  28.     if(dis[e]!= inf)  
  29.         printf("%d\n",dis[e]);  
  30.     else  
  31.         printf("-1\n");  
  32. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值