Dijkstra算法是一种最短路径路由算法,用于求单源最短路径,但其要求边权不能为负值。
bellman-ford算法也是用来去单源最短路径,但是其边权可以负值。
spfa算法为bellman-ford的算法的优化版本,在有些情况下能够实现较小的时间复杂度。
核心实现部分。
Dijkstra:利用贪心的思想,每次加入最近的未访问节点,从而得到最短路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | int dijkstra() {          for(int i=1;i<=n;i++)     {         dist[i] = inf;     }          dist[1] = 0;
           for (int i = 0; i < n; ++i) {         int t = -1;                  for (int j = 1; j <= n; ++j) {                                       if (!st[j] && (t == -1 || dist[j] < dist[t])) {                 t = j;             }         }
                   st[t] = true;
                   for (int j = 1; j <= n; ++j) {             dist[j] = min(dist[j], dist[t] + g[t][j]);         }     }
           if (dist[n] == inf) return -1;          return dist[n]; }
 
  | 
 
Dijkstra 还可以进行堆优化,可使用STL优先队列。
从O(n2)优化为O(nlogn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | int dijstrkual() {     dist[1] =0;     pq.push({0,1});     while(!pq.empty())     {         auto t = pq.top();         pq.pop();         int distance = t.first;         int u = t.second;         if(vis[u])continue;         else vis[u] = true;         for(auto&v:gra[u])         {             int y = v.first;             int d = v.second;             if(dist[y]>distance+d)             {                 dist[y] = distance+d;                 pq.push({dist[y],y});             }         }     }     return dist[n]; }
 
  | 
 
bellman-ford:循环遍历图中的所有边,对图中的边进行松弛操作,从而得到可能的最短路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | int bellman_ford() {     dist[1]=0;     for(int i=0;i<k;i++)     {         vector<int>last = dist;         for(int j=0;j<=m;j++)         {             int a = edges[j][0];             int b = edges[j][1];             int w = edges[j][2];             dist[b] = min(dist[b],last[a]+w);         }     }     return dist[n];      }
 
  | 
 
spfa:SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,它的优化思路是利用了搜索的思想,只将对更新了距离的节点加入队列,因为只有更新了距离的节点才对后续的松弛操作有影响。
优化方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | int spfa() {     queue<int>q;     q.push(1);     dist[1]=0;     vis[1]=true;     while(!q.empty())     {         int u = q.front();         q.pop();         vis[u]=false;         for(auto&vp:gra[u])         {             int v = vp.first;             int d = vp.second;             if(dist[v]>dist[u]+d)             {                 dist[v] = dist[u]+d;                 if(!vis[v])                 {                     q.push(v);                     vis[v] =true;                 }             }         }     }     return dist[n]; }
 
  | 
 
和Dijkstra很像,但Dijkstra是贪心,每次更新一点,而spfa是对所有的边都进行松弛。
spfa还可以进行判断负环操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | bool spfa() {     queue<int>q;     for(int i=1;i<=n;i++)     {         q.push(i);     }     while(!q.empty())     {         int u= q.front();         q.pop();         vis[u] =false;         for(auto&vp:gra[u])         {             int v = vp.first;             int d = vp.second;             if(dist[v]>dist[u]+d)             {                 dist[v] =dist[u]+d;                 cnt[v] = cnt[u]+1;                 if(cnt[v]>=n) return true;                 if(!vis[v])                 {                     q.push(v);                     vis[v] = true;                 }             }         }     }     return false; }
 
  | 
 
Floyd算法可以用来解决多源最短路径问题,它的思想基于动态规划。
从点i到点j的最短路径,要么由i直接到j,要么是由i经过点k再到达点j。点i到点j的最小值只能从这两种状态转移过来,从而建立动态规划转移方程。
1 2 3 4 5 6 7 8 9 10 11 12 13
   | void floyd() {     for(int k=1;k<=n;k++)     {         for(int i=1;i<=n;i++)         {             for(int j=1;j<=n;j++)             {                 d[i][j] =min(d[i][j],d[i][k]+d[k][j]);             }         }     } }
 
  |