定义 | 含义 |
---|
最短路径(Shortest Path) | 在网络中,求两个不同顶点之间的所有路径中,边权重值之和最小的那一条路径 |
源点(Source) | 第一个顶点 |
终点 | 最后一个顶点(Destination) |
单源最短路径问题 | 从固定源点出发到其他所有顶点的最短路径 |
多源最短路问题 | 求任意两顶点之间的最短路径 |
1. 无权图的单源最短路问题
分析 |
---|
路径的长度和走过的节点的数量有关 |
利用BFS的特性把所有的点全部搜索一遍 |
在搜索的过程中记录dist(距离)和path(路径) |
dist也充当了visited的作用 |
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
vector<vector<int>> rode;
vector<int> dist;
vector<int> path;
int N, M;
void Unweighted(int v)
{
fill(dist.begin(), dist.end(), -1);
queue<int> q;
dist[v] = 0;
q.push(v);
while (!q.empty())
{
int x = q.front();
q.pop();
for (auto e : rode[x])
{
if (dist[e] == -1)
{
dist[e] = dist[x] + 1;
path[e] = x;
q.push(e);
}
}
}
}
int main()
{
cin >> N >> M;
rode.resize(N + 5);
path.resize(N + 5);
dist.resize(N + 5);
for (int i = 0; i < M; i++)
{
int x, y;
cin >> x >> y;
rode[x].push_back(y);
}
Unweighted(3);
cout << "v :dist\n";
for (int i = 1; i <= N; i++)
cout << i << " :" << dist[i] << endl;
int v = 7;
while (v != 0)
{
cout << v << ' ';
v = path[v];
}
return 0;
}
2. 有权图的单源最短路问题 Dijkstra算法
分析 |
---|
最短路径和节点的个数无关 |
陈越老师的mooc |
1. 定义个dist数组 意思是距离源点的最短路径,注意的是在程序的过程中dist的值不是唯一的,即最后的dist才的最短的,在运行中,有可能是某一条路径的权重,但不一定是这个点的最短路 |
2. 先从源点A开始,访问A的邻接点,并设置他们的dist |
3. 现在已经有了第一批顶点,就这些顶点里面找一个权重最小的顶点,再去访问他的邻接点 |
4. 假如这一批顶点里有个点是B,B有一个邻接点C,C的dist现在已经是有值了,因为在上一步B访问C的时候,已经算出了C的权重,可是现在dist[C]不一定是最后的到C的最短路,因为有可能还有别的点也能到C,且权重比dist[C]小,那个时候就得更新dist[C]了。注意:现在的dist[B]是已经确定的了(因为第3步里是找的权重最小的点)。 |
5. 就这样依次循环即可 |
3.2 还有一件事情就是怎么选一个权重最小的点 可以用一个最小堆 我用的是优先队列 好些一点 |
时间复杂度: |
邻接矩阵 O(N*N) |
邻接表+优先队列 O(N + ElogN) |
优先队列
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
using pii = pair<int, int>;
vector<int> dist;
vector<int> path;
int N, M;
struct node
{
int from, to, weight;
};
vector<vector<node>> rode;
void Dijkstra(int v)
{
fill(dist.begin(), dist.end(), 0x3f3f3f3f);
fill(path.begin(), path.end(), -1);
dist[v] = 0;
priority_queue<pii, vector<pii>> q;
q.push({0, v});
while (!q.empty())
{
pii x = q.top();
q.pop();
int d = x.first;
int v = x.second;
for (auto e : rode[v])
{
if (dist[v] + e.weight < dist[e.to])
{
dist[e.to] = dist[v] + e.weight;
path[e.to] = v;
q.push({dist[e.to], e.to});
}
}
}
}
int main()
{
int star;
cin >> N >> M >> star;
rode.resize(N + 5);
path.resize(N + 5);
dist.resize(N + 5);
for (int i = 0; i < M; i++)
{
int x, y, d;
cin >> x >> y >> d;
rode[x].push_back({x, y, d});
}
Dijkstra(star);
cout << "v :dist\n";
for (int i = 1; i <= N; i++)
cout << i << " :" << dist[i] << " " << path[i] << endl;
return 0;
}
多源
含义 |
---|
构建一个二维矩阵 |
初始为权重 没有直接路径的两点为无穷 对角线为0 |
我们都知道矩阵的行i和j i->j 对应的是i到j路径 |
而路径也有可能是通过另一个k 再到j |
即 i->k1->k2->k3->…->j 这个就是i->j的最短路 |
方法就是全部试一试 |
看是i->j 权重小还是 i->k->j 权重小 |
而这些个k是哪一个就得遍历了 |
把每个元素都当成k遍历一遍 |
大家看代码可能不怎么理解:k看着只有一个啊,怎么做到有好几个k呢 |
其实i->j 是一直在变化的 在程序运行过程中我们不知道现在的i->j 是不是i直接到j的路径 |
很有可能是已经走了几条路了 现在的i->j 就是目前的最短路径 |
#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 1e3;
const int inf = 0x3f3f3f3f;
int rode[MAX][MAX];
int path[MAX][MAX];
int N, M;
void Floyd()
{
memset(path, -1, sizeof(path));
for (int k = 1; k <= N; k++)
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
if (rode[i][k] + rode[k][j] < rode[i][j])
{
rode[i][j] = rode[i][k] + rode[k][j];
path[i][j] = k;
}
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= N; j++)
cout << rode[i][j] << " ";
cout << endl;
}
}
int main()
{
cin >> N >> M;
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= N; j++)
{
if (i != j)
rode[i][j] = inf;
else
rode[i][j] = 0;
}
}
for (int i = 0; i < M; i++)
{
int x, y, d;
cin >> x >> y >> d;
rode[x][y] = d;
}
Floyd();
return 0;
}