正如我們所知道的,floyd演算法用於求最短路徑。floyd演算法可以說是warshall演算法的擴充套件,三個for迴圈就可以解決問題,所以它的時間複雜度為o(n^3)。
floyd演算法的基本思想如下:從任意節點a到任意節點b的最短路徑不外乎2種可能,1是直接從a到b,2是從a經過若干個節點x到b。所以,我們假設dis(ab)為節點a到節點b的最短路徑的距離,對於每乙個節點x,我們檢查dis(ax) + dis(xb) < dis(ab)是否成立,如果成立,證明從a到x再到b的路徑比a直接到b的路徑短,我們便設定dis(ab) = dis(ax) + dis(xb),這樣一來,當我們遍歷完所有節點x,dis(ab)中記錄的便是a到b的最短路徑的距離。
很簡單吧,**看起來可能像下面這樣:
for
(
int
i = 0; i < 節點個數; ++i )
}
}
}
但是這裡我們要注意迴圈的巢狀順序,如果把檢查所有節點x放在最內層,那麼結果將是不正確的,為什麼呢?因為這樣便過早的把i到j的最短路徑確定下來了,而當後面存在更短的路徑時,已經不再會更新了。
讓我們來看乙個例子,看下圖:
圖中紅色的數字代表邊的權重。如果我們在最內層檢查所有節點x,那麼對於a->b,我們只能發現一條路徑,就是a->b,路徑距離為9。而這顯然是不正確的,真實的最短路徑是a->d->c->b,路徑距離為6。造成錯誤的原因就是我們把檢查所有節點x放在最內層,造成
過早的把a到b的最短路徑確定下來了,當確定a->b的最短路徑時dis(ac)尚未被計算。所以,我們需要改寫迴圈順序,如下:
for
(
int
k = 0; k < 節點個數; ++k )
}
}
}
這樣一來,對於每乙個節點x,我們都會把所有的i到j處理完畢後才繼續檢查下乙個節點。
那麼接下來的問題就是,我們如何找出最短路徑呢?這裡需要借助乙個輔助陣列path,它是這樣使用的:path(ab)的值如果為p,則表示a節點到b節點的最短路徑是a->...->p->b。這樣一來,假設我們要找a->b的最短路徑,那麼就依次查詢,假設path(ab)的值為p,那麼接著查詢path(ap),假設path(ap)的值為l,那麼接著查詢path(al),假設path(al)的值為a,則查詢結束,最短路徑為a->l->p->b。
那麼,如何填充path的值呢?很簡單,當我們發現dis(ax) + dis(xb) < dis(ab)成立時,就要把最短路徑改為a->...->x->...->b,而此時,path(xb)的值是已知的,所以,path(ab) = path(xb)。
好了,基本的介紹完成了,接下來就是實現的時候了,這裡我們使用圖以及鄰接矩陣:
#define infinite 1000 // 最大值
#define max_vertex_count 20 // 最大頂點個數
//
struct
graph
;
//
首先,我們寫乙個方法,用於讀入圖的資料:
void
readgraphdata( graph *_pgraph )
}
}
接著,就是核心的floyd演算法:
void
floyd(
int
_arrdis[max_vertex_count],
int
_arrpath[max_vertex_count],
int
_nvertexcount )
}
//
for
(
int
k = 0; k < _nvertexcount; ++k )
}
}
}
}
ok,最後是輸出結果資料**:
void
printresult(
int
_arrdis[max_vertex_count],
int
_arrpath[max_vertex_count],
int
_nvertexcount )
else
while
( k != i );
//
std::cout << stackvertices.top()+1;
stackvertices.pop();
unsigned
int
nlength = stackvertices.size();
for
( unsigned
int
nindex = 0; nindex < nlength; ++nindex )
std::cout <<
" -> "
<< j+1 << std::endl;
}
}
}
}
}
好了,是時候測試了,我們用的圖如下:
測試**如下:
int
main(
void
)
}
floyd( arrdis, arrpath, mygraph.nvertexcount );
//
printresult( arrdis, arrpath, mygraph.nvertexcount );
//
system
(
"pause"
);
return
0;
}
如圖:
演算法學習 Union Find演算法
union find演算法有它的實際用途。多用於動態連通的應用場景。union find演算法是 給出兩個節點,判斷它們是否連通,如果連通,是不需要給出具體的路徑的 舉兩個例子作為主要表現 1 在網路連線中,當發現沒有連線的兩個節點,可以把他們連線起來,一旦節點都連線起來,又能把多餘的線拆除,這時候...
演算法學習 KM演算法
km演算法 用於求二分圖的最佳完美匹配 即權值最大的完美匹配 如果你也是個剛來學習km演算法的人 大概的用途肯定還是知道的吧 還是直接說重點吧 首先 理解km演算法前 必須有以下3個概念 1.可行頂標 對於乙個賦值二分圖g x,y,e,w x,y 代表二分圖的兩邊頂點標號 e代表邊 w代表邊的權值 ...
演算法 演算法學習01
貪婪 可以理解為最簡單基礎的求解方式,特點是 短視性 從這個特點入手很容易理解每一步取其最優的原理。雖然最終結果不一定是最好的,但是一定是較好的而且是最簡便的。因此在不過分追求最優結果或者對速度的要求高於結果的情況下,貪婪是不錯的選擇。分治可以理解為大事化小小事好搞,與貪婪的每一步的 串聯 不同,分...