• Bellman-Ford算法的改进---SPFA算法


    传送门:

     Dijkstra

     Bellman-Ford

     SPFA

     Floyd

    1.算法思想

    Bellman-Ford算法时间复杂度比较高,在于Bellman-Ford需要递推n次,每次递推需要扫描所有的边,在递推n次的过程中,很多判断是多余的,所以考虑用队列优化,减少不必要的判断,这种算法称为SPFA(Shortest Path Faster Algorithm)

    SPFA算法的大致流程就是用一个队列来进行维护,初始时将源点加入队列,每次从队列中取出一个顶点,并对它所有相邻的节点进行松弛,如果某个顶点松弛成功,则将其入队,重复这样的过程,直至队列为空为止。时间复杂度在O(Km)(通常K为2左右)一个顶点可以多次入队,但是如果有顶点入队次数大于n次,那就存在负环,此时应当返回存在负环信息

    2.算法过程

    在SPFA算法中同样可以用dist数组表示最短路长度,path数组保存路径,还需要设置cnt数组记录入队次数,vis数组记录当前是否在队列中

    (1).取出队列头结点u,扫描从顶点u出发的每条边,设每条边的终点为v,边的权值为w(u, v)。如果dist[u] + w  <  dist[v],则将dist[v]修改成dist[u] + w<u, v>。修改path[v] = u,如果顶点v不在队列中,还需要将v加入队列并且入队次数加一。如果上述条件不成立就不做任何处理

    (2).重复1直至队列为空或者某个顶点入队次数大于n

    3.算法实现

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 #include<queue>
      7 #include<stack>
      8 #include<map>
      9 #include<set>
     10 #include<sstream>
     11 using namespace std;
     12 typedef long long ll;
     13 const int maxn = 1000 + 10;
     14 const int INF = 1 << 25;
     15 int T, n, m, cases;
     16 struct Edge
     17 {
     18     int u, v, w;
     19     Edge(){}
     20     Edge(int u, int v, int w):u(u), v(v), w(w){}
     21 };
     22 vector<Edge>edges;//把每一条边存下来
     23 vector<int>Map[maxn];//G[i]这个vector存的是以i为起点的所有边在edges里面的下标
     24 void init(int n)
     25 {
     26     for(int i = 0; i <= n; i++)Map[i].clear();
     27     edges.clear();
     28 }
     29 void addedge(int u, int v, int w)
     30 {
     31     edges.push_back(Edge(u, v, w));//注意无向图需要存两条边
     32     m = edges.size();
     33     Map[u].push_back(m - 1);
     34 }
     35 void Find(int u)//遍历以u为起点的所有边
     36 {
     37     for(int i = 0; i < Map[u].size(); i++)
     38     {
     39         Edge&e = edges[Map[u][i]];
     40         //使用e就可以遍历以u为起点的所有的边
     41     }
     42 }
     43 int cnt[maxn];
     44 bool vis[maxn];
     45 int d[maxn], path[maxn];
     46 bool SPFA(int u)
     47 {
     48     queue<int>q;
     49     memset(vis, 0, sizeof(vis));//初始化
     50     memset(cnt, 0, sizeof(cnt));
     51     memset(path, -1, sizeof(path));
     52     for(int i = 0; i < n; i++)d[i] = INF;
     53     d[u] = 0;
     54     vis[u] = 1;//标记进入队列
     55     q.push(u);
     56     while(!q.empty())
     57     {
     58         int u = q.front();
     59         q.pop();
     60         vis[u] = 0;//清除进入队列标记
     61         for(int i = 0; i < Map[u].size(); i++)
     62         {
     63             Edge& e = edges[Map[u][i]];
     64             if(d[u] < INF && d[e.v] > d[u] + e.w)
     65             {
     66                 d[e.v] = d[u] + e.w;
     67                 path[e.v] = Map[u][i];//path存的是边的下标,这样可以通过边找出之前的点以及每条路的路径,如果用邻接矩阵存储的话这里可以直接存节点u
     68                 if(!vis[e.v])
     69                 {
     70                     q.push(e.v);
     71                     vis[e.v] = 1;
     72                     if(++cnt[e.v] > n)return true;//进队次数大于n,说明存在负环
     73                 }
     74             }
     75         }
     76     }
     77     for(int i = 0; i < n; i++)
     78     {
     79         if(i == u)continue;
     80         printf("从%d到%d距离是:%2d   ", u, i, d[i]);
     81         stack<int>q;//存的是边的编号
     82         int x = i;//x就是路径上所有的点
     83         while(path[x] != -1)
     84         {
     85             q.push(x);
     86             x = edges[path[x]].u;//x变成这条边的起点
     87         }
     88         cout<<u;
     89         while(!q.empty())
     90         {
     91             cout<<"->"<<q.top();
     92             q.pop();
     93         }
     94         cout<<endl;
     95     }
     96     return false;
     97 }
     98 int main()
     99 {
    100     int c;
    101     cin >> n >> c;
    102     int u, v, w;
    103     for(int i = 0; i < c; i++)
    104     {
    105         cin >> u >> v >> w;
    106         addedge(u, v, w);
    107     }
    108     if(SPFA(0))cout<<"存在负环"<<endl;
    109     else cout<<"不存在负环"<<endl;
    110     return 0;
    111 }
  • 相关阅读:
    Autofac-案例
    Autofac-DynamicProxy(AOP动态代理)
    AutoFac注册2-程序集
    MVC添加跨域支持Cros
    redis笔记3-基础知识与常用命令
    Redis笔记2-Redis安装、配置
    Redis笔记-八种数据类型使用场景
    ActionResult源码分析笔记
    .NET UrlRouting原理
    webapi使用ExceptionFilterAttribute过滤器
  • 原文地址:https://www.cnblogs.com/fzl194/p/8728883.html
Copyright © 2020-2023  润新知