1. 程式人生 > >Bellman-Ford算法的改進---SPFA算法

Bellman-Ford算法的改進---SPFA算法

維護 html class href 一個 下標 註意 優化 所有

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 }

Bellman-Ford算法的改進---SPFA算法