一般最短路径算法习惯性的分为两种:单源最短路径算法和全顶点之间最短路径。前者是计算出从一个点出发,到达所有其余可到达顶点的距离。后者是计算出图中所有点之间的路径距离。
单源最短路径
Dijkstra算法
思维
本质上是贪心的思想,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,此时完成一个顶点, 然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。 然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。
举例演算
集合S | 当前观察点u | dis[2] | dis[3] | dis[4] | dis[5] | dis[6] |
---|---|---|---|---|---|---|
1 | - | 1 | ∞ | 2 | ∞ | ∞ |
1,2 | 2 | 1 | ∞ | 2 | 4 | 6 |
1,2,4 | 4 | 1 | 5 | 2 | 4 | 6 |
1,2,4,5 | 5 | 1 | 5 | 2 | 4 | 6 |
1,2,4,5,3 | 3 | 1 | 5 | 2 | 4 | 5 |
1,2,4,5,3,6 | 6 | 1 | 5 | 2 | 4 | 5 |
从结点1出发,1与2、4连通,确定(1,2),(1,4)的距离,其中到2的距离最短,再从观察点2出发,2与5、6连通,根据dis[u]+c[u][v]<dis[v]的判断关系出发,更新dis。接着再分别从观察点4,5,3,6出发更新dis,得到最终的结点1的单源最短路径。
代码实现
为了能够方便的寻找当前最小的dis作为观察点,可以利用优先队列最小堆来优化。这里直接给出优化后的代码。
利用邻接矩阵存图
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int MAX = 1000;
int im[MAX][MAX], dis[MAX], n, m;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
void insert(int u,int v,int c) {
im[u][v] = c;
im[v][u] = c;
}
void dij(int s) {
for (int i = 0; i < MAX; i++) dis[i] = 0x7FFFFFFF;
dis[s] = 0;
que.push(make_pair(dis[s], s));
while (!que.empty()) {
pair<int, int> u = que.top();
que.pop();
for (int i = 1; i <= n; i++) {
if (im[u.second][i]) {
if (dis[i] > dis[u.second] + im[u.second][i]) {
dis[i] = dis[u.second] + im[u.second][i];
que.push(make_pair(dis[i], i));
}
}
}
}
}
int main() {
cin >> n >> m;
memset(im, 0, sizeof(im));
int u, v, c;
for (int i = 0; i < m; i++) {
cin >> u >> v >> c;
insert(u, v, c);
}
int x;
cin >> x;
dij(x);
for (int i = 1; i <= n; i++) {
if (dis[i] = 0x7FFFFFFF) cout << "none" << " ";
else cout << dis[i] << " ";
}
cout << endl;
return 0;
}
邻接矩阵比较消耗空间,也可以利用表的形式来存储图。
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int MAX = 1000;
int h[MAX * 2], to[MAX * 2], nxt[MAX * 2], co[MAX * 2], dis[MAX], k = 0, n, m;
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
void insert(int u,int v,int c) {
nxt[++k] = h[u];
h[u] = k;
to[k] = v;
co[k] = c;
nxt[++k] = h[v];
h[v] = k;
to[k] = u;
co[k] = c;
}
void dij(int s) {
for (int i = 0; i < MAX; i++) dis[i] = 0x7FFFFFFF;
dis[s] = 0;
que.push(make_pair(dis[s], s));
while (!que.empty()) {
pair<int, int> u = que.top();
que.pop();
for (int i = h[u.second]; i; i = nxt[i]) {
if (dis[to[i]] > dis[u.second] + co[i]) {
dis[to[i]] = dis[u.second] + co[i];
que.push(make_pair(dis[to[i]], to[i]));
}
}
}
}
int main() {
cin >> n >> m;
memset(h, 0, sizeof(h));
int u, v, c;
for (int i = 0; i < m; i++) {
cin >> u >> v >> c;
insert(u, v, c);
}
int x;
cin >> x;
dij(x);
for (int i = 1; i <= n; i++) {
if (dis[i] > 100000) cout << "none" << " ";
else cout << dis[i] << " ";
}
cout << endl;
return 0;
}