zoukankan      html  css  js  c++  java
  • 最短路问题的三种算法&模板

    最短路算法&模板

    最短路问题是图论的基础问题。本篇随笔就图论中最短路问题进行剖析,讲解常用的三种最短路算法:Floyd算法、Dijkstra算法及SPFA算法,并给出三种算法的模板。流畅阅读本篇博客需要有图论的基础知识,了解什么是图,什么是最短路,以及一些基本语法知识和算法基础。

    1、Floyd算法

    我个人认为,Floyd算法是三种最短路算法中最简单、最好理解的算法。它的适用范围是任意两点之间的最短路。这一点是其他两种算法(单源最短路)无法比拟的。它的实现思路也很简单:用三重循环,枚举断点、起始点和终点(注意:顺序千万不能反!!),如果起始点到断点,断点到终点的距离和小于起始点到终点当前状态下的最短路(也就是说找到了一个比它还短的),那么就更新最短路。

    它的优点就是简洁明了,易于理解,但是缺点也显而易见,通过它的实现途径,我们可以发现,使用Floyd算法的题一定要用邻接矩阵存图,这样的一个二维数组显然对空间有着要求,一般来讲,只能支持不超过500个点的图,假如更多,便无法支持。同时,Floyd算法还对时间有着要求,因为是三重循环,所以它的时间复杂度是(O(n^3))的,这样的复杂度如果出现在一个复杂程序中,极其容易TLE,所以,请大家使用的时候,一定要读题读题,慎重慎重!

    模板:

    void Floyd()
    {
        memset(map,0x3f,sizeof(map));
        for(int i=1;i<=n;i++)
            map[i][i]=0;
        for(int k=1;k<=n;k++)//顺序不要反
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    map[i][j]=min(map[i][k]+map[k][j],map[i][j]);
    }
    

    2、Dijkstra算法

    Dijkstra算法,中文名是迪杰斯特拉算法,简写是DIJ算法。DIJ算法是求解单源最短路,即从某一个源点到达其他所有点的最短路的一个经典算法。它的实现途径也很好理解:我把点分成两个集合,一个是已经扫描过的集合,另一个是没有扫描的集合。即已经确定最短路和没确定最短路的两个集合,用v数组标记。然后我们从没有扫描的集合中挑出一个dist最小的点放入已经扫描的集合中,然后从这个点开始搜索它的所有出边,进行松弛操作即可。

    DIJ算法的时间复杂度比较高,大约是(O(n^2))级别的,同SPFA比确实是差了一些,但它有自己独特的优势,由于它的复杂度只和点有关,所以针对于稠密图(边多点少的图),它的求解效率要比SPFA算法高很多。

    关于DIJ算法可以使用二叉堆(优先队列)进行优化,即我们常说的DIJ算法堆优化,由于篇幅较长,我放到了另一篇博客中进行讲解,链接:

    DIJ算法堆优化

    模板:

    void dijkstra(int start)//源点
    {
        int temp,k,y;
        memset(dist,0x3f,sizeof(dist));
        memset(v,0,sizeof(v));
        dist[start]=0;
        for(int i=1;i<=n;i++)
        {
            temp=1<<30;
            for(int j=1;j<=n;j++)
                if(dist[j]<temp && v[j]==0)
                    k=j,temp=dist[j];
            v[k]=1;
            for(int j=head[k];j;j=nxt[j])
            {
                y=to[j];
                if(dist[y]>dist[k]+val[j])
                    dist[y]=dist[k]+val[j];
            }
    	}
    }
    

    3、SPFA算法

    网上有一张热图,就是“关于SPFA,它死了”。但是SPFA并没有死,它不仅活的好好的,还是一种国产算法,更是我最擅长的最短路算法

    SPFA的适用范围同样也是单源最短路,但是它的实现途径较抽象:新建一个队列,保存等待优化的节点,把源点加入队列。取出队首节点x,遍历x的所有出边,进行松弛操作,如果x点的到达点y被更新,且y不在当前队列中,就把y压入队尾。重复直到队列空为止。

    SPFA的实现模板跟宽搜很类似,希望大家好好体会。

    SPFA的时间复杂度也很优越,根据证明,期望的时间复杂度可以达到(O(kM)),其中M为边数,k为所有顶点进队的平均次数。一般来讲,k<=2。

    所以我们发现,SPFA算法适用于稀疏图,即点特别多边特别少的图。这和DIJ算法形成了一个对比,希望大家能在做题的时候活学活用。

    关于SPFA的算法,有两种优化方式,由于篇幅较长,放在另一个博客中进行讲解。链接如下:

    SPFA算法的优化

    模板:

    void spfa(int start)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(v,0,sizeof(v));
        queue<int> q;
        q.push(start);
        v[start]=1;
        dist[start]=0;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            v[x]=0;
            for(int i=head[x];i;=nxt[i])
            {
                int y=to[i];
                if(dist[y]>dist[x]+val[i])
                {
                    dist[y]=dist[x]+val[i];
                    if(v[y]==0)
                        q.push(y),v[y]=1;
                }
    		}
    	}
    }
    
  • 相关阅读:
    git pull遇到错误:error: Your local changes to the following files would be overwritten by merge:
    angular 过滤器(日期转换,时间转换,数据转换等)
    js 毫秒转天时分秒
    使用Vue-Router 2实现路由功能
    vue-cli中安装方法
    Vue 2.5 发布了:15篇前端热文回看
    es6 语法 (模块化)
    es6 语法 (Decorator)
    es6 语法 (Generator)
    js 判断当前是什么浏览器
  • 原文地址:https://www.cnblogs.com/fusiwei/p/11389702.html
Copyright © 2011-2022 走看看