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