C++圖論核心之最短路徑算法從原理到實(shí)戰(zhàn)
一、Dijkstra算法
最短路徑問(wèn)題是指,從在帶權(quán)的有向圖中從某一頂點(diǎn)出發(fā),找到通往另一頂點(diǎn)的最短路徑,“最短”指的是沿路徑各邊的權(quán)值總和最小。
Dijkstra算法是單源最短路徑的經(jīng)典貪心算法,只能用于沒(méi)有負(fù)權(quán)的圖。它從起點(diǎn)出發(fā),每次選當(dāng)前距離最小且未確定最短路徑的節(jié)點(diǎn),用它去松弛(更新)所有鄰接點(diǎn)的最短路徑估計(jì)值,標(biāo)記該節(jié)點(diǎn)為 “已確定”,重復(fù)此過(guò)程直到所有節(jié)點(diǎn)處理完畢,最終得到起點(diǎn)到圖中所有節(jié)點(diǎn)的最短路徑。

// src是選定的起點(diǎn),dist記錄起點(diǎn)到各點(diǎn)的最短路徑,pPath記錄到每個(gè)點(diǎn)的最短路徑的前驅(qū)頂點(diǎn)下標(biāo)
void Dijkstra(const V& src, vector<W>& dist, vector<int>& pPath)
{
size_t srci = GetVertexIndex(src);
size_t n = _vertexs.size();
dist.resize(n, MAX_W);
pPath.resize(n, -1);
dist[srci] = 0;
pPath[srci] = srci;
// 已經(jīng)確定最短路徑的頂點(diǎn)集合
vector<bool> S(n, false);
for (size_t j = 0; j < n; ++j)
{
// 選最短路徑頂點(diǎn)且不在S更新其他路徑
int u = 0;
W min = MAX_W;
for (size_t i = 0; i < n; ++i)
{
if (S[i] == false && dist[i] < min)
{
u = i;
min = dist[i];
}
}
S[u] = true;
// 松弛更新u連接頂點(diǎn)v srci->u + u->v < srci->v 更新
for (size_t v = 0; v < n; ++v)
{
if (S[v] == false && _matrix[u][v] != MAX_W
&& dist[u] + _matrix[u][v] < dist[v])
{
dist[v] = dist[u] + _matrix[u][v];
pPath[v] = u;
}
}
}
}二、Bellman_Ford算法
Bellman_Ford算法能用來(lái)解決負(fù)權(quán)圖的單源最短路徑問(wèn)題,但是它的時(shí)間復(fù)雜度高于Dijkstra算法,本質(zhì)是暴力求解。從起點(diǎn)出發(fā),把圖里所有邊從頭到尾松弛一遍,重復(fù)n次,就能算出起點(diǎn)到所有點(diǎn)的最短路徑;因?yàn)槿魏巫疃搪窂阶疃嘀唤?jīng)過(guò)n?1條邊。跑完之后再掃一遍所有邊,如果還能更新距離,就說(shuō)明圖里有負(fù)權(quán)回路,最短路徑不存在。

bool BellmanFord(const V& src, vector<W>& dist, vector<int>& pPath)
{
size_t n = _vertexs.size();
size_t srci = GetVertexIndex(src);
// vector<W> dist,記錄srci-其他頂點(diǎn)最短路徑權(quán)值數(shù)組
dist.resize(n, MAX_W);
// vector<int> pPath 記錄srci-其他頂點(diǎn)最短路徑父頂點(diǎn)數(shù)組
pPath.resize(n, -1);
// 先更新srci->srci為缺省值
dist[srci] = W();
// 總體最多更新n輪
for (size_t k = 0; k < n; ++k)
{
// i->j 更新松弛
bool update = false;
cout << "更新第:" << k << "輪" << endl;
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < n; ++j)
{
// srci -> i + i ->j
if (_matrix[i][j] != MAX_W && dist[i] != MAX_W && dist[i] + _matrix[i][j] < dist[j])
{
update = true;
//cout << _vertexs[i] << "->" << _vertexs[j] << ":" << _matrix[i][j] << endl;
dist[j] = dist[i] + _matrix[i][j];
pPath[j] = i;
}
}
}
// 如果這個(gè)輪次中沒(méi)有更新出更短路徑,那么后續(xù)輪次就不需要再走了
if (update == false)
{
break;
}
}
// 還能更新就是帶負(fù)權(quán)回路
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < n; ++j)
{
// srci -> i + i ->j
if (_matrix[i][j] != MAX_W && dist[i] + _matrix[i][j] < dist[j])
{
return false;
}
}
}
return true;
}三、Floyd_Warshall算法
Floyd-Warshall算法是求任意兩點(diǎn)之間最短路徑的算法,依次把每個(gè)點(diǎn)當(dāng)作中轉(zhuǎn)點(diǎn),判斷從 i 到 j 是直接走更近,還是經(jīng)過(guò)這個(gè)中轉(zhuǎn)點(diǎn) k 再走更近,不斷更新所有點(diǎn)對(duì)的最短距離,三層循環(huán)跑完就得到全圖最短路徑。
void FloydWarshall(vector<vector<W>>& vvDist, vector<vector<int>>& vvpPath)
{
size_t n = _vertexs.size();
vvDist.resize(n);
vvpPath.resize(n);
// 初始化權(quán)值和路徑矩陣
for (size_t i = 0; i < n; ++i)
{
vvDist[i].resize(n, MAX_W);
vvpPath[i].resize(n, -1);
}
// 直接相連的邊更新一下
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < n; ++j)
{
if (_matrix[i][j] != MAX_W)
{
vvDist[i][j] = _matrix[i][j];
vvpPath[i][j] = i;
}
if (i == j)
{
vvDist[i][j] = W();
}
}
}
// 最短路徑的更新i-> {其他頂點(diǎn)} ->j
for (size_t k = 0; k < n; ++k)
{
for (size_t i = 0; i < n; ++i)
{
for (size_t j = 0; j < n; ++j)
{
// k 作為的中間點(diǎn)嘗試去更新i->j的路徑
if (vvDist[i][k] != MAX_W && vvDist[k][j] != MAX_W
&& vvDist[i][k] + vvDist[k][j] < vvDist[i][j])
{
vvDist[i][j] = vvDist[i][k] + vvDist[k][j];
// 找跟j相連的上一個(gè)鄰接頂點(diǎn)
// 如果k->j 直接相連,上一個(gè)點(diǎn)就k,vvpPath[k][j]存就是k
// 如果k->j 沒(méi)有直接相連,k->...->x->j,vvpPath[k][j]存就是x
vvpPath[i][j] = vvpPath[k][j];
}
}
}
}
}以上就是C++圖論核心之最短路徑算法從原理到實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于C++最短路徑算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)投票系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)投票系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
關(guān)于C++使用std::chrono獲取當(dāng)前秒級(jí)/毫秒級(jí)/微秒級(jí)/納秒級(jí)時(shí)間戳問(wèn)題
這篇文章主要介紹了C++使用std::chrono獲取當(dāng)前秒級(jí)/毫秒級(jí)/微秒級(jí)/納秒級(jí)時(shí)間戳,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
C語(yǔ)言利用棧實(shí)現(xiàn)對(duì)后綴表達(dá)式的求解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言利用棧實(shí)現(xiàn)對(duì)后綴表達(dá)式的求解,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
MySQL系列教程之使用C語(yǔ)言來(lái)連接數(shù)據(jù)庫(kù)
c語(yǔ)言操作Mysql數(shù)據(jù)庫(kù),主要就是為了實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增、刪、改、查等操作,下面這篇文章主要給大家介紹了關(guān)于MySQL系列教程之使用C語(yǔ)言來(lái)連接數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09
c語(yǔ)言將字符串中的小寫(xiě)字母轉(zhuǎn)換成大寫(xiě)字母
本文主要介紹了c語(yǔ)言將字符串中的小寫(xiě)字母轉(zhuǎn)換成大寫(xiě)字母的方法實(shí)例。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
C語(yǔ)言中強(qiáng)制類型轉(zhuǎn)換的常見(jiàn)方法
強(qiáng)制類型轉(zhuǎn)換是一種將一個(gè)數(shù)據(jù)類型轉(zhuǎn)換為另一個(gè)數(shù)據(jù)類型的方法,這篇文章主要為大家整理了C語(yǔ)言中強(qiáng)制類型轉(zhuǎn)換的方法,需要的可以參考一下2023-05-05
C++中強(qiáng)類型枚舉(scoped enumeration)的實(shí)現(xiàn)
C++11引入的強(qiáng)類型枚舉解決了傳統(tǒng)枚舉的三大缺陷:作用域污染、隱式類型轉(zhuǎn)換和底層類型不確定,本文就來(lái)詳細(xì)的介紹一下強(qiáng)類型枚舉的使用,感興趣的可以了解一下2025-11-11

