SPFA研究

说起来,SPFA这个算法之前一直是半懂不懂,偶尔敲一个模板,然后囫囵做个两三道水题,倒也是轻松,然而一旦遇到比较复杂的题目的时候总是摸不了门道,于是下了决心花几个小时一定要把它吃透。不过没有想到深入探究了一番,其实SPFA也不是很复杂,结合之前的最短路问题求解的相关知识,很快就能掌握其中的技巧,甚是欣喜。

简介:

SPFA就是使用队列或者栈的Bellman-Ford算法的优化版本。都知道Bellman-Ford算法的复杂度之高,不是在所有时候都适用。SPFA正是为了解决这个问题而生的。

SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它是Bellman-ford的队列优化,它是一种十分高效的最短路算法。

很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。SPFA的复杂度大约是O(kE),k是每个点的平均进队次数(一般的,k是一个常数,在稀疏图中小于2)。

但是,SPFA算法稳定性较差,在稠密图中SPFA算法时间复杂度会退化。

实现方法:建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。

此外,SPFA算法还可以判断图中是否有负权环,即一个点入队次数超过N。

参考
SPFA算法详解
最短路径 之 SPFA算法

常用数组

struct Node
{
    int to;                     //指向点标号
    int cost;                   //路径长度
    int next;                   //下一个点标号
}node[max_edges];               //max_edges为最大边数,储存每一边状态

int head[max_nodes];            //max_nodes为最大点数,储存邻接表头指针位置
int vis[max_nodes];             //记录节点在队列中是否存在
int dis[max_nodes];             //储存最短路径长度

队列式SPFA(常见)

void SPFA(int start,int n)      //SPFA算法核心,start为起点标号,n为节点数量
{
    memset(vis,0,sizeof(vis));             //清空访问标记
    for ( int i = 1 ; i <= n ; i++){       //初始化距离为INF
          d[i] = INF;
    }
    dis[start] = 0                //将起始位置距离设置为0
    queue<int>Q;
    Q.push(start);                //将初始位置推入队列中
    vis[start] = 1;               //标记在队列中初始位置出现过

    while(!Q.empty())
    {
        int x = Q.front();
        Q.pop();
        vis[x] = 0;             //将节点从队列退出后要记得将访问标记设置为0

        for(int i = f[x] ; i != -1 ; i = next[i])   //访问该节点下的所有分支节点
        {
            int t = node[i].to;
            if(d[t] > d[x] + node[i].cost)    //松弛操作
            {
                d[t] = d[x] + node[i].cost;
                if(!vis[t])                //判断节点是否在队列中
                {
                    Q.push(t);
                    vis[t] = 1;
                }
            }
        }
    }
}

添加操作

void add(int u,int v,int c)
{
    node[cnt].to = v ;//指向节点
    node[cnt].cost = c;//路径长度
    node[cnt].next = head[u] ;  //构建邻接表
    head[u] = cnt++;
}

初始化操作

void init()
{
     int cnt = 0;                 //cnt为边的数量,添加操作
    memset(head,-1,sizeof(head));         
 //初始化头指针位置为-1方便SPFA中对于每一个节点搜索下一个节点的结束
}

栈型SPFA(占用内存更少,运行效率更高,用数组模拟更佳)

void SPFA(int sta,int n)
{
    stack<int>S;
    for(int i = 1 ; i <= n ; i++){
        if(i == sta){
            S.push(i);
            vis[i] = true;
            dis[i] = 0;
        }
        else
        {
            vis[i] = false;
            dis[i] = INF;
        }
    }

    while(!S.empty())
    {
        int u = S.top();
        S.pop();
        vis[u] = false;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(dis[v] > dis[u] + edge[i].v)
            {
                dis[v] = dis[u] + edge[i].v;
                if(!vis[v]){
                    vis[v] = true;
                    S.push(v);
                }
            }
        }
    }
}
    原文作者:Bellman-Ford
    原文地址: https://www.jianshu.com/p/b72d97241425
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞