zoukankan      html  css  js  c++  java
  • 图论,最短路径问题总结

    维基百科定义最短路:

    一个有6个节点和7条边的图

    最短路径问题图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

    • 确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法
    • 确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。
    • 确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径。
    • 全局最短路径问题 - 求图中所有的最短路径。适合使用Floyd-Warshall算法

    用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”。 最常用的路径算法有:


    1.floyd算法  (弗洛伊德)(n^3复杂度)


    基本思想:开始设集合S的初始状态为空,然后依次将0,1,。。n-1定点加入,同时用d[i][j]保存从i到j,仅经过S中的定点的最短路径,在初始时刻,d[i][j] = A[i][j]中间不经过任何节点,然后依次向S中插入节点,并进行如下更新
    d(k)[i][j] = min{  d(k-1)[i][j] ,  d(k-1)[i][k]+d(k-1)[k][j]  }
    还可以使用一个二维数组path指示最短路径。
    path[i][j]给出从定点i到j的最短路径上,定点i的前一个顶点
    代码相当简单,最容易的实现方法:

    for (k = 0;k < n;k++)
    for (i = 0;i < n;i++)
    for (j = 0;j < n;j++)
    {
    	if (d[i][k] + d[k][j] < d[i][j])
    	{
    		d[i][j] = d[i][k] + d[k][j];
    		path[i][j] = path[k][j];
    	}
    }

    可以通过递推得出路径的。。

    2.dijstra (迪杰斯特拉算法)算法

    单源最短路问题,先加入源,维持一张表来保存此时到源中的最短距离,选取最小的加入,然后更新表,不断的加入直到目的地在源中。仅适用于正边权的时侯,因为这时我们可以保证任意加入的点已经找到了源到该点的距离。

     

    3.bellman-ford算法

     

    最优性原理

    最优性原理介绍及简单证明见:http://blog.csdn.net/liguanxing/article/details/7401798

    它是最优性原理的直接应用,算法基于以下事实:

    如果最短路存在,则每个顶点最多经过一次,因此不超过n-1条边;

    长度为k的路由长度为k-1的路加一条边得到;

    由最优性原理,只需依次考虑长度为1,2,…,k-1的最短路。

     

    适用条件&范围

    单源最短路径(从源点s到其它所有顶点v);

    有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);

    边权可正可负(如有负权回路输出错误提示);

     差分约束系统(需要首先构造约束图,构造不等式时>=表示求最小值,作为最长路,<=表示求最大值,作为最短路。<=构图时,有负环说明无解;求不出最短路(为Inf)为任意解。>=构图时类似)。  

     

    算法描述

     1)对每条边进行|V|-1次Relax操作;

     2)如果存在(u,v)∈E使得dis[u]+w<dis[v],则存在负权回路;否则dis[v]即为s到v的最短距离,pre[v]为前驱。  

     

    for i:=1 to |V|-1 do //进行|v|-1次松弛得最短距离
        for 每条边(u,v)∈E do   
            Relax(u,v,w);
    for每条边(u,v)∈E do //判断是否存在负权环
        if dis[u]+w<dis[v] 
            Then Exit(False)
    

    算法时间复杂度O(VE)。因为算法简单,适用范围又广,虽然复杂度稍高,仍不失为一个很实用的算法。  

    改进和优化  如果循环n-1次以前已经发现不存在紧边则可以立即终止;

     

     

    4.spfa算法

    SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。

     

     

    算法流程  

    SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,算法大致流程是用一个队列来进行维护,即用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出队首顶点,对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列 queue 和一个指示顶点是否在队列中的标记数组mark。为了方便查找某个顶点的邻接点,图采用临界表存储。


    Procedure SPFA;
     
    Begin
       initialize-single-source(G,s);
       initialize-queue(Q);
       enqueue(Q,s);
       while not empty(Q) do begin
          u:=dequeue(Q);
          for each v∈adj[u] do begin
             tmp:=d[v];
             relax(u,v);
             if (tmp<>d[v]) and (not v in Q) then enqueue(Q,v);
             end;
          end;
    End;

    注意:spfa算法只有在不存在负权环的情况下可以正常的结束,如果存在负权环,那么将总有顶点在入队和出队往返,队列无法为空,这种情况下SPFA无法正常结束。可以通过添加一个变量表示每个顶点进入队列的次数,如果大于|v|那么就可以说明存在负权环



  • 相关阅读:
    Scala基础(1)
    简单模拟flume
    朴素贝叶斯
    关于hive的优化
    Hive的一些理解
    Flume的简单理解
    tiny-Spring【2】逐步step分析-新加入特性
    前、中、后缀表达式【待完成】
    奇妙的算法【9】YC每个小孩的糖果数,找公约数,最少硬币数
    奇妙的算法【8】筹钱种数、定时找出最高频次的数据、三子棋落点判断
  • 原文地址:https://www.cnblogs.com/tham/p/6827285.html
Copyright © 2011-2022 走看看