最短路--SPFA

 

SPFA 算法

 

算法优点

        1.时间复杂度比普通的Dijkstra和Ford

        2.能够计算负权图问题。

        3.能够判断是否有负环 (即:每跑一圈,路径会减小,所以会一直循环跑下去)。

 

 

 

 

期望的时间复杂度O(k*e), 其中k为所有顶点进队的平均次数,e是边的数量,可以证明k一般小于等于2。

 

实现方法:

  1.存入图。可以使用链式前向星或者vocter

        2.开一个队列,先将开始的节点放入。

        3.每次从队列中取出一个节点X,遍历与X相通的Y节点,查询比对  Y的长度X的长度+ X与Y的长度

            如果X的长度+ X与Y的长度 Y的长度,说明需要更新操作。

                    1).存入最短路。

                    2).由于改变了原有的长度,所以需要往后更新,与这个节点相连的最短路。(即:判断下是否在队列,在就不用重复,不在就加入队列,等待更新)。

                    3).在这期间可以记录这个节点的进队次数,判断是否存在负环。

        4.直到队空。

 

判断有无负环:如果某个点进入队列的次数超过N次则存在负环

 

 

 

模拟过程:

 

 

首先建立起始点a到其余各点的最短路径表格

                                  

首先源点a入队,当队列非空时:

        1、队首元素(a)出队,对以a为起始点的所有边的终点依次进行松弛操作(此处有b,c,d三个点),此时路径表格状态为:

                                  

 

在松弛时三个点的最短路径估值变小了,而这些点队列中都没有出现,这些点需要入队,此时,队列中新入队了三个结点b,c,d

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e点),此时路径表格状态为:

                                 

 

在最短路径表中,e的最短路径估值也变小了,e在队列中不存在,因此e也要入队,此时队列中的元素为c,d,e

队首元素c点出队,对以c为起始点的所有边的终点依次进行松弛操作(此处有e,f两个点),此时路径表格状态为:

                                 

 

在最短路径表中,e,f的最短路径估值变小了,e在队列中存在,f不存在。因此e不用入队了,f要入队,此时队列中的元素为d,e,f

 队首元素d点出队,对以d为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

                               

在最短路径表中,g的最短路径估值没有变小(松弛不成功),没有新结点入队,队列中元素为f,g

队首元素f点出队,对以f为起始点的所有边的终点依次进行松弛操作(此处有d,e,g三个点),此时路径表格状态为:

                               

 

在最短路径表中,e,g的最短路径估值又变小,队列中无e点,e入队,队列中存在g这个点,g不用入队,此时队列中元素为g,e

队首元素g点出队,对以g为起始点的所有边的终点依次进行松弛操作(此处只有b点),此时路径表格状态为:

                           

 

在最短路径表中,b的最短路径估值又变小,队列中无b点,b入队,此时队列中元素为e,b队首元素e点出队,对以e为起始点的所有边的终点依次进行松弛操作(此处只有g这个点),此时路径表格状态为:

 

                          

 

在最短路径表中,g的最短路径估值没变化(松弛不成功),此时队列中元素为b

队首元素b点出队,对以b为起始点的所有边的终点依次进行松弛操作(此处只有e这个点),此时路径表格状态为:

                         

 

在最短路径表中,e的最短路径估值没变化(松弛不成功),此时队列为空了

最终a到g的最短路径为14

以上转载自:http://keyblog.cn/article-21.html

 

代码

int way[250][250],dis[250],vis[250],cnt[250];
//way记录路径关系,dis[i]记录起点到点j的最近距离,vis[i]标记点是否在队列中,cnt[i]记录点i进入队列的次数
int n,m;
void init()
{
    for(int i=0;i<n;i++)//先初始化way
    {
        for(int j=0;j<n;j++)
        {
            if(i==j)
                way[i][j]=0;
            else
                way[i][j]=mx;
        }
    }
}

void spfa(int st)//st是起点
{
    for(int i=0;i<n;i++)//这里点编号是从0开始的
        dis[i]=mx;
    memset(vis,0,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    vis[st]=1;
    cnt[st]=1;
    dis[st]=0;
    queue<int>p;
    p.push(st);
    while(!p.empty())
    {
        int now=p.front();
        p.pop();
        vis[now]=0;
        for(int i=0;i<n;i++)
        {
            if(dis[now]+way[now][i]<dis[i])
            {
                dis[i]=dis[now]+way[now][i];
                if(vis[i]==0)//如果点不在队列里面
                {
                    p.push(i);
                    vis[i]=1;
                    cnt[i]++;
                    if(cnt[i]>n)//如果这个点加入超过n次,说明存在负圈,直接返回 
                        return ;
                }
            }
        }
    }

}

 

模板题:https://www.cnblogs.com/-citywall123/p/11324215.html

 

转载于:https://www.cnblogs.com/-citywall123/p/11324246.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值