網站首頁 編程語言 正文
什么是最短路徑問題
如果從圖中某一頂點(稱為源點)到達另一頂點(稱為終點)的路徑可能不止一條,如何找到一條路徑使得沿此路徑上各邊上的權值總和達到最小。
單源最短路徑問題是指對于給定的圖G=(V,E),求源點v0到其它頂點vt的最短路徑。
Dijkstra算法
Dijkstra算法用于計算一個節點到其他節點的最短路徑。Dijkstra是一種按路徑長度遞增的順序逐步產生最短路徑的方法,是一種貪婪算法。
Dijkstra算法的核心思想是首先求出長度最短的一條最短路徑,再參照它求出長度次短的一條最短路徑,依次類推,直到從源點v0到其它各頂點的最短路徑全部求出為止。
具體來說:圖中所有頂點分成兩組,第一組是已確定最短路徑的頂點,初始只包含一個源點,記為集合S;第二組是尚未確定最短路徑的頂點,記為集合U。
按最短路徑長度遞增的順序逐個把U中的頂點加到S中去,同時動態更新U集合中源點到各個頂點的最短距離,直至所有頂點都包括到S中。
實現思路
1.初始時,S集合只包含起點v0;U集合包含除v0外的其他頂點vt,且U中頂點的距離為起點v0到該頂點的距離。(U 中頂點vt的距離為(v0,vt)的長度,如果v0和vt不相鄰,則vt的最短距離為∞)
2.從U中選出距離最短的頂點vt′,并將頂點vt′加入到S中;同時,從U中移除頂點vt′。
3.更新U中各個頂點vt到起點v0的距離以及最短路徑中當前頂點的前驅頂點。之所以更新U中頂點的距離以及前驅頂點是由于上一步中確定了vt′是求出最短路徑的頂點,從而可以利用vt′來更新U中其它頂點vt的距離,因為存在(v0,vt)的距離可能大于(v0,vt')+(vt',vt)距離的情況,從而也需要更新其前驅頂點
4.重復步驟(2)和(3),直到遍歷完所有頂點
案例分析
代碼實現
使用了部分C++11特性,注釋豐富,讀起來應該不會太困難!
#include <cstdio>
#include <iostream>
#include <vector>
#include <list>
#include <stack>
using namespace std;
using Matrix = vector<vector<uint>>; // 連接矩陣(使用嵌套的vector表示)
using SNodes = vector<tuple<uint, uint, uint>>; // 已計算出最短路徑的頂點集合S(類似一個動態數組)
using UNodes = list<tuple<uint, uint, uint>>; // 未進行遍歷的頂點集合U(使用list主要是方便元素刪除操作)
using ENode = tuple<uint, uint, uint>; // 每個節點包含(頂點編號,當前頂點到起始點最短距離,最短路徑中當前頂點的上一個頂點)信息
/***
* 從未遍歷的U頂點集合中找到下一個離起始頂點距離最短的頂點
* @param unvisitedNodes 未遍歷的U頂點集合
* 每個元素是(頂點編號,當前頂點到起始點最短距離,最短路徑中當前頂點的上一個頂點)的tuple
* @return 下一個離起始頂點距離最短的頂點
*/
ENode searchNearest(const UNodes &unvisitedNodes) {
uint minDistance = UINT_MAX;
ENode nearest;
for (const auto &node: unvisitedNodes) {
if (get<1>(node) <= minDistance) {
minDistance = get<1>(node);
nearest = node;
}
}
return nearest;
}
/***
* 迪克斯特拉算法的實現
* @param graph 連接矩陣(使用嵌套的vector表示)
* @param startNodeIndex 起始點編碼(從0開始)
* @return 返回一個vector,每個元素是到起始頂點的距離排列的包含(頂點編號,當前頂點到起始點最短距離,最短路徑中當前頂點的上一個頂點)的tuple
*/
SNodes dijkstra(const Matrix &graph, uint startNodeIndex) {
const uint numOfNodes = graph.size(); // 圖中頂點的個數
// S是已計算出最短路徑的頂點的集合(頂點編號,當前頂點到起始點最短距離,最短路徑中當前頂點的上一個頂點)
SNodes visitedNodes;
// U是未計算出最短路徑的頂點的集合(其中的key為頂點編號,value為到起始頂點最短距離和最短路徑中上一個節點編號組成的pair)
UNodes unvisitedNodes;
// 對S和U集合進行初始化,起始頂點的距離為0,其他頂點的距離為無窮大
// 最短路徑中當前頂點的上一個頂點初始化為起始頂點,后面會逐步進行修正
for (auto i = 0; i < numOfNodes; ++i) {
if (i == startNodeIndex) visitedNodes.emplace_back(i, 0, startNodeIndex);
else unvisitedNodes.emplace_back(i, graph[startNodeIndex][i], startNodeIndex);
}
while (!unvisitedNodes.empty()) {
// 從U中找到距離起始頂點距離最短的頂點,加入S,同時從U中刪除
auto nextNode = searchNearest(unvisitedNodes);
unvisitedNodes.erase(find(unvisitedNodes.begin(), unvisitedNodes.end(), nextNode));
visitedNodes.emplace_back(nextNode);
// 更新U集合中各個頂點的最短距離以及最短路徑中的上一個頂點
for (auto &node: unvisitedNodes) {
// 更新的判斷依據就是起始頂點到當前頂點(nextNode)距離加上當前頂點到U集合中頂點的距離小于原來起始頂點到U集合中頂點的距離
// 更新最短距離的時候同時需要更新最短路徑中的上一個頂點為nextNode
if (graph[get<0>(nextNode)][get<0>(node)] != UINT_MAX &&
graph[get<0>(nextNode)][get<0>(node)] + get<1>(nextNode) < get<1>(node)) {
get<1>(node) = graph[get<0>(nextNode)][get<0>(node)] + get<1>(nextNode);
get<2>(node) = get<0>(nextNode);
}
}
}
return visitedNodes;
}
/***
* 對使用迪克斯特拉算法求解的最短路徑進行打印輸出
* @param paths vector表示的最短路徑集合
* 每個元素是到起始頂點的距離排列的包含(頂點編號,當前頂點到起始點最短距離,最短路徑中當前頂點的上一個頂點)的tuple
*/
void print(const SNodes &paths) {
stack<int> tracks; //從尾部出發,使用stack將每個頂點的最短路徑中的前一個頂點入棧,然后出棧的順序就是最短路徑順序
// 第一個元素是起始點,從第二個元素進行打印輸出
for (auto it = ++paths.begin(); it != paths.end(); ++it) {
// 打印頭部信息
printf("%c -> %c:\t Length: %d\t Paths: %c",
char(get<0>(paths[0]) + 65),
char(get<0>(*it) + 65),
get<1>(*it),
char(get<0>(paths[0]) + 65));
auto pointer = *it;
// 如果當前指針pointer指向的節點有中途節點(判斷的條件是最短路徑中的前一個節點不是起始點)
while (get<2>(pointer) != get<0>(paths[0])) {
tracks.push(get<0>(pointer));
// Lambda表達式,使用find_if函數把當前頂點的前一個頂點從paths中找出來繼續進行循環直到前一個節點就是起始點
auto condition = [pointer](tuple<uint, uint, uint> x) { return get<0>(x) == get<2>(pointer); };
pointer = *find_if(paths.begin(), paths.end(), condition);
}
tracks.push(get<0>(pointer));
// 以出棧的順序進行打印輸出
while (!tracks.empty()) {
printf(" -> %c", char(tracks.top() + 65));
tracks.pop();
}
printf("\n");
}
}
int main() {
Matrix graph = {
{0, 12, UINT_MAX, UINT_MAX, UINT_MAX, 16, 14},
{12, 0, 10, UINT_MAX, UINT_MAX, 7, UINT_MAX},
{UINT_MAX, 10, 0, 3, 5, 6, UINT_MAX},
{UINT_MAX, UINT_MAX, 3, 0, 4, UINT_MAX, UINT_MAX},
{UINT_MAX, UINT_MAX, 5, 4, 0, 2, 8},
{16, 7, 6, UINT_MAX, 2, 9, 9},
{14, UINT_MAX, UINT_MAX, UINT_MAX, 8, 9, 0}
}; // 圖對應的連接矩陣
auto results = dijkstra(graph, uint('D' - 65)); // 選取頂點C(大寫字母A的ASCII編碼是65)
print(results); // 打印輸出結果
return 0;
}
運行結果:
D -> C:?? ? Length: 3?? ? Paths: D -> C
D -> E:?? ? Length: 4?? ? Paths: D -> E
D -> F:?? ? Length: 6?? ? Paths: D -> E -> F
D -> G:?? ? Length: 12?? ? Paths: D -> E -> G
D -> B:?? ? Length: 13?? ? Paths: D -> C -> B
D -> A:?? ? Length: 22?? ? Paths: D -> E -> F -> A
原文鏈接:https://blog.csdn.net/theonegis/article/details/108110380
相關推薦
- 2022-05-21 淺談GO中的Channel以及死鎖的造成_Golang
- 2022-10-23 C++進程的創建和進程ID標識詳細介紹_C 語言
- 2022-10-06 C++?pimpl機制詳細講解_C 語言
- 2022-09-21 使用注解實現Redis緩存功能_Redis
- 2022-06-14 python?多線程threading程序詳情_python
- 2022-08-30 MongoD管理數據庫的方法介紹_MongoDB
- 2023-04-07 React?styled?components樣式組件化使用流程_React
- 2022-07-14 React?Native?Popup實現示例_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支