最短路问题的三种算法&模板

最短路算法&模板

最短路问题是图论的基础问题。本篇随笔就图论中最短路问题进行剖析,讲解常用的三种最短路算法: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;
            }
		}
	}
}
posted @ 2019-08-21 16:52  Seaway-Fu  阅读(1339)  评论(1编辑  收藏  举报