新人对SPFA算法的理解

最近刚刚学习了一下SPFA算法,发现其实也是不大难的,也许只是会者不难吧

下面我来谈一下作为博客园新人对于SPFA算法的理解,我会给出c++代码

首先 SPFA 算法是用来求最短路径的,实际上这个算法在国际中并不被认可,但是它的确是一个高效的最短路算法,它是Bellman-Ford算法的优化版本,时间复杂度为O(kE),k的值一般情况下比较小,所以比较适合来求最短路径。

接下来,讲一下SPFA算法的思想。

SPFA算法的思想大概是这样的(这里直接用链表来讲吧)

创建一个链表来存储题目给出边的信息,这是SPFA执行之前必须的工作,否则会发现,要么非常难写,要么写的不对,时间效率也下来了

在c++中有两种链表储存方式,一种是手写数组,另一种则是STL模版库里面的vector不定长数组,代码在讲解结束的时候再与SPFA核心代码一并给出

接下来是SPFA的核心思想:

首先建立一个d数组来记录每个节点i到起点s的最短路径长度,将d数组所有初始值全部赋为正无穷(其实在写程序的时候,自己定义一个比较大的数INF然后都赋值成INF就好了)

创建一个队列,由于本人习惯使用STL中的queue队列,所以,本人的代码中会有queue出现。queue的基本操作:q.push(value):将元素value入队,无返回值;q.front():取出队列首部元素,有返回值;q.pop():弹出队列首部元素,无返回值;

将d[s]赋值成为0,并以节点s扩展更新与s相邻节点到s的最短距离,并将更新成功的节点入队,弹出s,并且每次用队列的首元素更新与这个元素相邻的节点的d[i]值,并将成功更新的节点加入队列。(这里要注意,有可能被更新成功的节点已经在队列中,这时就无需再次将其加入队列,只需将被成功更新并且没有在队列中的节点入队即可)并且使用队列首元素更新完成后将取出的队列首元素弹出队列,这样循环一直到队列为空的时候停止。

在这里举个例子吧:比如在一个图中,有1、2、3、4、5这5个节点,现在以1为起始点,1加入队列q,并记录1已经在队列中,1更新了2、3、4,那么就将2、3、4加入队列,(此时d[2]、d[3]、d[4]已经更新),将1弹出队列,并且设置1不在队列中,接下来取2更新,检索到了3、4、5,成功更新了4、5,,4无需再进队列,5进队列,弹出2,设置2不在队列中,再用3去更新,此时更新到了2,那么2再次进入队列,这样不断循环,直到q为空为止;

下面给出代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #include <queue>
 7 #define maxn 1001
 8 #define INF 100000000
 9 using namespace std;
10 struct edge{
11     int from,to,dist;
12     edge(int a,int b,int c):from(a),to(b),dist(c){};
13 };
14 vector<edge>edges;
15 vector<int>f[maxn];
16 int n,m,j,k,l;
17 int d[maxn];
18 bool v[maxn];//queue_visited[1001];
19 int spfa(int s){//核心代码 
20     memset(v,false,sizeof(v));
21     for(int i = 1;i <= n;++i){
22         d[i] = INF;
23     }
24     queue<int>q;
25     d[s] = 0;
26     v[s] = true;
27     q.push(s);
28     while(!q.empty()){
29         int x = q.front();
30         q.pop();
31         v[x] = 0;
32         for(int i = 0;i < f[x].size();++i){
33             edge o = edges[f[x][i]];
34             if(o.dist + d[x] < d[o.to]){
35                 d[o.to] = o.dist + d[x];
36                 if(!v[o.to]){
37                     v[o.to] = 1;
38                     q.push(o.to); 
39                 }
40             }
41         }
42     }
43     return d[n];
44 }
45 int main(){
46     scanf("%d%d", &n, &m);
47     for(int i = 1;i <= m;++i){//链表存储 
48         cin >> j >> k >> l;
49         edges.push_back(edge(j,k,l));
50         edges.push_back(edge(k,j,l));
51         int count = edges.size();
52         f[j].push_back(count-2);
53         f[k].push_back(count-1);  
54     }
55     int ans = spfa(1);
56     cout<<ans<<endl;
57     return 0;
58 }

 

当题目明确可能有负环的时候,可以通过一个元素进入队列次数来判断,如果一个元素进入队列超过n次,则说明有负环

当然我用了一些STL的东西,不用STL的话,请读者自己思考一下

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值