Dijkstra算法–單源最短路徑
可計算一個圖中某一結點到任意結點的最短路徑。
它通過更新一維數組dis
的數值來計算最短路徑長度,還可以通過使用一維數組parent
來記錄到達結點i
之前需要到達的結點parent[i]
(似鏈表)。
算法要點:
-
一個圖中有
n
個結點,結點值1 2 ... n
,輸入一個起點結點s
,則初始化dis[s] = 0
(結點s到結點s的距離爲0),其他的結點i
有dis[i] = inf
(結點s到其他結點的距離爲無窮大)。 -
通過邊表
e
找到以s
爲起點的結點到其他結點間的距離e[s][i]
,同時將s
加入到已經找到了最短路徑的集合P
,在此用映射mp[s]=1
表示。若dis[i]>dis[s]+e[s][i]
,則更新dis[i]=dis[s]+e[s][i]
,此過程稱爲鬆弛(relax)。找到不屬於集合P
的各結點中dis[i]
最小的那個結點i
(距離結點s
最近的的結點),如果最小的dis[i]=inf
,則不繼續計算;反之,將結點i
作爲新的s
進行計算。(這部分和參考書不完全一致) -
parent
數組的長度和dis
相同,任意結點i
初始化爲parent[i]=-1
,在計算最短路徑dis[i]
的過程中更新parent[i]
的數值。輸出路徑時注意數據結構及順序。
#include<iostream>
#include<vector>
#include<map>
using namespace std;
const int inf = 999999999;
int n, m;
vector<int> dis, parent;
vector<vector<int>> e;
map<int, int> mp;
void dijkstra(int s) {
int min = inf, mindis = inf;
mp[s] = 1; // 已經找到了最短路徑的結點就標記爲1
for (int i = 1; i < n+1; i++) {
if (mp[i] == 0) { // 未標記過的結點
if (e[s][i] != inf && dis[i] > dis[s] + e[s][i]) {
dis[i] = dis[s] + e[s][i];
parent[i] = s;
if (mindis > dis[i]) { //找到此次relax得到的最小dis
mindis = dis[i];
min = i;
}
}
}
}
if (min == inf) { // 此次沒有relax到任何邊
return;
}
dijkstra(min);
}
int main () {
int t1, t2, t3, from, to;
cin >> n >> m;
e.resize(n+1, vector<int>(n+1));
dis.resize(n+1);
parent.resize(n+1);
// 初始化
for (int i = 1; i < n+1; i++) {
dis[i] = inf;
parent[i] = -1;
for (int j = 1; j < n+1; j++) {
if (i == j) {
e[i][j] = 0;
} else {
e[i][j] = inf;
}
}
}
for (int i = 0; i < m; i++) {
cin >> t1 >> t2 >> t3;
// 有向圖
e[t1][t2] = t3;
}
cout << "from:";
cin >> from; // 輸入起點結點
dis[from] = 0;
dijkstra(from);
// dis數組 記錄了該起點到任意結點間的最短路徑長度
cout << "dis:\t";
for (int i = 1; i < n+1; i++) {
cout << dis[i] << " ";
}
// parent數組 記錄了到達結點i之前需要先到達的結點parent[i]
cout << endl << "parent:\t";
for (int i = 1; i < n+1; i++) {
cout << parent[i] << " ";
}
cout << endl << "to:";
cin >> to; // 輸入終點結點
// 輸出路徑
vector<int> path;
while (parent[to] != -1) { // 逆序遍歷
path.push_back(to);
to = parent[to];
}
if (path.size() == 0) { // 不可達
cout << "inaccessible";
return 0;
}
path.push_back(from);
cout << "path:\t";
for (int i = path.size()-1; i >= 0; i--) { // 輸出路徑
cout << path[i] << " ";
}
return 0;
}
input:
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
1
6
output:
dis: 0 1 8 4 13 17
parent: -1 1 4 2 3 5
path: 1 2 4 3 5 6