Bellman-Ford算法,是單源最短路算法的一種。c++
與以前的 Dijkstra算法 最大的不一樣是:Dijkstra算法沒法判斷含負權邊的圖的最短路,而Bellman-Ford算法能夠處理 存在負權邊 的最短路徑。算法
因爲Bellman-Ford算法簡單地對全部邊進行鬆弛操做,共|V|-1次。因此這個算法的時間效率較低,也正是它的不足之處。性能
Bellman-Ford的時間複雜度: O(V*E) (V,E分別是點數 與 邊數)優化
圖解樣例(來源《算法導論》):code
僞代碼:blog
INIT(G,s); for i=1 to |G.V|-1 for each edge(u,v)∈G.E RELAX(u,v,w) for each edge(u,v)∈G.E if v.d>u.d+w(u,v) return FLASE return TRUE
代碼以下(鄰接矩陣):隊列
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define MAXN 1010 int n,m,ori; //點,邊,起點 struct Edge{ int from,to,cost; }edge[MAXN]; //鄰接矩陣 int dis[MAXN]; bool Bellman_Ford() { for(int i=1;i<=n;++i) dis[i]=(i==ori ? 0 : INF); //初始化 for(int i=1;i<=n-1;++i) //n-1 for(int j=1;j<=m;++j) { int u=edge[j].from,v=edge[j].to; if(dis[v]>dis[u]+edge[j].cost) dis[v]=dis[u]+edge[j].cost; } bool flag=1; //判斷是否含有負環 //原理:負權環能夠無限制的下降總權值,因此若是發現第 n次操做仍可下降總權值,就必定存在負權環。 for(int i=1;i<=m;++i) { int u=edge[i].from,v=edge[i].to; if(dis[v]>dis[u] + edge[i].cost) {flag = 0;break;} } return flag; } int main() { std::scanf("%d%d%d",&n,&m,&ori); for(int i=1;i<=m;++i) std::scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].cost); if(Bellman_Ford()) //先判斷是否有負環 //這裏也能夠再加一句判斷是否連通,依題而定 std::printf("%d", dis[n]); else std::printf("have negative circle\n"); return 0; }
SPFA(Shortest Path Faster Algorithm):ci
Dijkstra有隊列優化,Bellman-Ford也有。SPFA是Bellman-Ford的隊列優化,減小了沒必要要的冗餘計算。it
算法流程:用一個隊列來進行維護。初始時將源加入隊列。每次從隊列中取出一個元素,並對全部與他相鄰的點進行鬆弛,若某個相鄰的點鬆弛成功,則將其入隊。直到隊列爲空時算法結束。ast
代碼以下(鄰接表):
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define MAXN 1010 int n,m,ori; //點,邊,起點 struct EDGE{int to,val,nxt;}e[MAXN]; //鄰接表 int adj[MAXN],dis[MAXN],cnt=0,num[MAXN];//計數器 bool vis[MAXN]={0}; //判斷是否在隊列 std::queue < int > q; void addedge(int u,int v,int w) //鏈式前向星 { e[++cnt].val=w; e[cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt; } bool SPFA(int ori) { for(int i=1;i<=n;++i) dis[i]=(i==ori ? 0 : INF); q.push(ori); vis[ori]=1; ++num[ori];//起點入隊列時記得+1 while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=adj[u];i;i=e[i].nxt) { int v=e[i].to; if(dis[v]>dis[u]+e[i].val) { dis[v]=dis[u]+e[i].val; if(!vis[v]) { vis[v]=1;q.push(v); ++num[v];//記錄加入次數 if(num[v]>n) return 0; //若是這個點加入超過n次,說明存在負環,直接返回 } } } } return 1; } int main() { std::scanf("%d%d%d",&n,&m,&ori); for(int i=1;i<=m;++i) { int u,v,w; std::scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } if(SPFA(ori)); std::printf("%d", dis[n]); return 0; }
代碼風格其實和Dijkstra算法很像(代碼思想相似BFS)。惟一的區別就是SPFA中須要有計算器來判斷是否存在負環。
對於隨機數據而言,時間複雜度:O(kE)(k爲一個較小系數)
但SPFA能夠人爲的造數據,卡負環,致使其性能變得很是低。(時間複雜度達到指數級)
因此,若是題目中不存在負權邊,用Dijkstra算法最爲保險。
作題感悟: