最短路径:我的理解--SPFA算法

SPFA算法
  求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm 最短路径快速算法-SPFA算法是西南交通大学段凡丁于1994年发表的。
  适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
  算法思想:我们用数组d记录每个结点的最短路径估计值,用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
最短路径:我的理解--SPFA算法
因为SPFA没有向迪杰斯塔拉算法那样,寻找dist[]的最小值,所以重复入队,更新dis[]的最小值,因为这个点本身dis[]的变化,会影响到与之邻接的点,所以要重复入队。
标程一:
const int INF = 999999;
int   map [ MAXN ][ MAXN ] ; //map[i,j] 为初始输入的 i j 的距离,未知的 map[i,j]=INF;
int   dis [ MAXN ] ; //源点 S i 的最短路
char vst [ MAXN ] ; //是否在队列中的标记
int      Q[MAXN]; // 队列
// 参数 n 表示结点数, s 表示源点
int SPFA ( int n, int s )
{   int i, pri, end, p, t ; // pri 是队列头结点, end 是队列尾结点
    memset ( vst, 0 , sizeof ( vst )) ; //初始化
    for ( int i = 0 ; i <</b>MAXN; ++i)
        Q [ i ] = 0 ; //初始化队列为空
    for ( i = 0 ; i <</b>n; i++)
        dis [ i ] = INF ; //初始化源点到 I 的值为最大值
    dis [ s ] = 0 ; //源点为 0
    vst [ s ] = 1 ; //标记为已入队
Q [ 0 ] = s ;//源点入队
pri = 0 ; end = 1 ; //队首队尾赋值
    while ( pri <</b> end)
    {
        p = Q [ pri ] ; //取队首元素
        for ( i = 0 ; i <</b>n; ++i) //更新dis
        { if ( dis [ p ] + map [ p ][ i ] <</b> dis[i])
            {   dis [ i ] = dis [ p ] + map [ p ][ i ] ;
                if ( ! vst [ i ])      // 未在队列中
                {   Q [ end ++ ] = i ;
                   vst [ i ] = 1 ;
                }
            }
        }
        vst [ p ] = 0 ;    // 置出队的点为未标记
        pri ++ ;
    }
    return 1 ;
}
标程二:
int num[999999]; //记录入队次数  
void spfa(int s)  //  初始结点s,即为起点,若目标结点t,则输出dict[t]。
{   init_data(s);
    int head = 0, tail = 1;  
    int path[Max];  //  可增加一个path[]数组来记录最后s到t的路径。
    queue[0] = s; //que.push(s);  
    dict[s] = 0;
    while (tail > head)//(!que.empty())
 
    { int u = queue[head];//int u=que.front();   //que.pop();
        vis[u] = true;  
        for (i = 1; i <= n; i ++)
        { if (dict[i] > dict[u] + edge[u][i])
            {  dict[i] = dict[u] + edge[u][i];
               path[i] = u;  
               num[i]++
               if(num[i]>=n) return 1;//判断是否有负权值……
                if (!vis[i])  //  对以前没有访问过的顶点,加入队列中。
                {  vis[i] = true;
                   queue[tail] = i;// que.push(i);
                    tail ++;                        
                }
              }
         }
          vis[u] = false;  //  记得把出队列的顶点的vis[]置为false。
        head ++;  
    }
}
判断负权回路 num[i]>=n的原因,即使所有的点更新都会让i入队的话,才只有n-1次,这时一定是最小值了,入队n次,一定有负权回路

转载于:https://www.cnblogs.com/c1299401227/p/5370769.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值