一.Floyd算法html
用於計算任意兩個節點之間的最短路徑。算法
參考了five20的博客數據結構
Floyd算法的基本思想以下:從任意節點A到任意節點B的最短路徑不外乎2種可能,1是直接從A到B,2是從A通過若干個節點到B,因此,咱們假設dist(AB)爲節點A到節點B的最短路徑的距離,對於每個節點K,咱們檢查dist(AK) + dist(KB) < dist(AB)是否成立,若是成立,證實從A到K再到B的路徑比A直接到B的路徑短,咱們便設置 dist(AB) = dist(AK) + dist(KB),這樣一來,當咱們遍歷完全部節點K,dist(AB)中記錄的即是A到B的最短路徑的距離。函數
標準五行代碼以下:優化
for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(dis[i][k]+dis[k][j]<dis[i][j]) { dis[i][j]=dis[i][k]+dis[k][j]; } }
可是這裏咱們要注意循環的嵌套順序,若是把檢查全部節點K放在最內層,那麼結果將是不正確的,爲何呢?由於這樣便過早的把i到j的最短路徑肯定下來了,而當後面存在更短的路徑時,已經再也不會更新了。spa
更多關於Floyd算法,詳細請見:Floyd算法百度百科連接.net
二.Dijkstra算法:code
適用於權值爲非負的圖的單源最短路徑。htm
斐波那契堆優化的時間複雜度爲O(E+VlogV),但其實只是理論值,實際中基本上達不到。
blog
算法思路:
參考了殷天文的博客
指定一個節點,例如咱們要計算 'A' 到其餘節點的最短路徑
引入兩個集合(S、U),S集合包含已求出的最短路徑的點(以及相應的最短長度),U集合包含未求出最短路徑的點(以及A到該點的路徑,注意 如上圖所示,A->C
因爲沒有直接相連 初始時爲∞)
初始化兩個集合,S集合初始時 只有當前要計算的節點,A->A = 0
,
U集合初始時爲 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞
,敲黑板!!!接下來要的兩個步驟是核心!
從U集合中找出路徑最短的點,加入S集合,例如 A->D = 2
更新U集合路徑,if ( 'D 到 B,C,E 的距離' + 'AD 距離' < 'A 到 B,C,E 的距離' )
則更新U
循環執行 四、5 兩步驟,直至遍歷結束,獲得A 到其餘節點的最短路徑
樸素版時間複雜度O(n²)算法代碼以下:
void Dijkstra() { memset(dis, 0x1f, sizeof(dis)); dis[1] = 0; for (int i=1; i<=n; i++) { int min_len = 1e9, k = 0; for (int j=1; j<=n; j++) if (!vis[j] && dis[j] < min_len) { min_len = dis[j]; k = j; } vis[k] = 1; for (int j=h[k]; j!=-1; j=edge[j].next) { int to = edge[j].to, w = edge[j].w; if (!vis[to] && dis[to] > dis[k]+w) { dis[to] = dis[k]+w; } } } }
穩定時間複雜度O(mlogn)的堆優化(優先隊列代替)代碼以下:
void Dijkstra_Heap() { priority_queue<pair<int, int> > q; memset(dis, 0x3f, sizeof(dis)); memset(vis,0,sizeof(v)); dis[1] = 0; q.push(make_pair(0, 1)); while (!q.empty()) { int now = q.top().second; q.pop(); if (vis[now]) continue; vis[now] = 1; for (int i=h[now]; i!=-1; i=edge[i].next) { int to = edge[i].to, w = edge[i].w; if (!vis[to] && dis[to]>dis[now]+w) { dis[to] = dis[now] + w; q.push(make_pair(-dis[to], to)); } } } }
這裏有一個關於優先隊列的小騷操做:
因爲STL中的優先隊列默認是大根堆,因此在使用push函數的時候只需在須要排序的數據前加個‘-’便可。
關於Dijkstra算法的選擇上,對於稀疏圖,因爲n和m比較接近,故選擇堆優化算法;而對於稠密圖,因爲點少邊多,故選擇樸素版算法。
更多關於Dijkstra算法,詳細請見:Dijkstra算法百度百科連接
三.Bellman-Ford算法
參考博客:圖解貝爾曼福特-算法
可用於解決如下問題:
算法思路:
思路上與狄克斯特拉算法(Dijkstra algorithm)最大的不一樣是每次都是從源點s從新出發進行"鬆弛"更新操做,而Dijkstra則是從源點出發向外擴逐個處理相鄰的節點,不會去重複處理節點,這邊也能夠看出Dijkstra效率相對更高點。
下面是有邊數k限制的Bellman_ford算法模板:
void Bellman_ford() { memset(dis,0x1f,sizeof(dis)); dis[1]=0; for(int i=1;i<=k;i++) { memcpy(last,dis,sizeof(last)); for(int j=1;j<=m;j++) { int u=edge[j].u,v=edge[j].v,w=edge[j].w; if(dis[v]>last[u]+w)dis[v]=last[u]+w; } } }
更多關於Bellman-Ford算法,詳細請見:Bellman-Ford算法百度百科連接
四.SPFA算法
SPFA是西安交通大學的段凡丁在1994年與《西安交通大學學報》中發表的「關於最短路徑的SPFA快速算法」,他在裏面說SPFA速度比Dijkstra快,且運行V次的SPFA速度比Floyd速度快。
而事實證實SPFA算法是有侷限的,他不適用於稠密圖,對於特別狀況的稠密圖,SPFA複雜度和BellmanFord時間同樣。
適用範圍:適用於權值有負值,且沒有負圈的圖的單源最短路徑,論文中的複雜度O(kE),k爲每一個節點進入Queue的次數,且k通常<=2,但此處的複雜度證實是有問題的,其實SPFA的最壞狀況應該是O(VE).
注意:SPFA算法在網格圖中很是慢
算法思路:
參考了小天位的博客
代碼以下:
void SPFA() { memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); queue<int> q; dis[1] = 0; vis[1] = 1; q.push(1); while (!q.empty()) { int now = q.front(); q.pop(); vis[now] = 0; for (int i=h[now]; i!=-1; i=edge[i].next) { int to = edge[i].to, w = edge[i].w; if (dis[now]+w < dis[to]) { //在隊列中的點也能夠進行鬆弛操做,故這裏不需加!ifq[to]的條件,而須要加在下面。 dis[to] = dis[now]+w; if (!vis[to]) q.push(to), vis[to] = 1; } } } }
更多關於SPFA算法,詳細請見:SPFA算法百度百科連接
五.最小環問題
解決思路:
最小環就是指在一張圖中找出一個環,使得這個環上的各條邊的權值之和最小。在Floyed的同時,能夠順便算出最小環。
記兩點間的最短路爲dis[i][j],g[i][j]爲邊<i,j>的權值。
一個環中的最大結點爲k(編號最大),與它相連的兩個點爲i,j,這個環的最短長度爲g[i][k]+g[k][j]+(i到j的路徑中,全部結點編號都小於k的最短路徑長度)。
根據Floyed的原理,在最外層循環作了k-1次以後,dis[i][j]則表明了i到j的路徑中,全部結點編號都小於k的最短路徑。
綜上所述,該算法必定能找到圖中最小環。
代碼以下:
參考了Coder_YX的博客
void floyd(){ int MinCost = inf; for(int k=1;k<=n;k++){ for(int i=1;i<k;i++) for(int j=i+1;j<k;j++) MinCost = min(MinCost,dis[i][j]+mp[i][k]+mp[k][j]);//更新k點以前枚舉ij求通過ijk的最小環 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //更新k點 } if(MinCost==inf)puts("It's impossible."); else printf("%d\n",MinCost); }
關於四種最短路算法的結論:
參考了xiazdong的博客
(1)當權值爲非負時,用Dijkstra。
(2)當權值有負值,且沒有負圈,則用SPFA,SPFA能檢測負圈,可是不能輸出負圈。
(3)當權值有負值,並且可能存在負圈,則用BellmanFord,可以檢測並輸出負圈。
(4)SPFA檢測負環:當存在一個點入隊大於等於V次,則有負環。