1、朴素Dijkstra算法
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2) 适合稠密图(邻接矩阵存) 边数=n^2
思路:
构造一个最短路径数组,每次找到数组中未访问的节点里最小的点
以上一步的节点为最新节点,更新起始点到所有点的距离
以此类推,直到所有点都被遍历。
伪代码:
初始化state数组(state数组保存所有点的 已确定的最短距离)
1号点的距离初始化为零,其他点初始化成无穷大:dist[1]=0 dist[other]=+∞
循环:每次循环都能确定一个点的最短距离,循环n次,求出每一个点到起点的最短距离
for j:0~n
遍历dist数组 找到一个没有确定最短路径的点state[j]=false,且该点距离源点最近
state[t]=true,标记点t已确定最短距离
用t更新他指向的其他点x的距离
判断从1走到x和从1走到t再走到x的距离,如果比较短,就更新x的距离
图片来自网络,侵删
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int n, m;
// 稠密图,邻接矩阵存图
int g[N][N];
// 表示每个点当前,从1号点走到他自己的最短距离
int dist[N];
// 每个点的最短路是否已经确定
int st[N];
int dijkstra()
{
// 初始化距离
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
// 有n个点所以要进行n次 迭代
for (int i = 0; i < n; i++)
{
int t = -1;
// 找到一个 距离源点最近的点,且这个点还没有确定最短路径st[j]=fasle
for (int j = 1; j <= n; j++)
{
// 当前这个点还没有确定最短路径
// 且
// 源点到当前这个点的距离是最短的
if (st[j] == false && (t == -1 || dist[t] > dist[j]))
{
// 找到了这个点j
t = j;
}
}
// 把t加到st里面去,标记这个点已经确定最短距离
st[t] = true;
// 用当前这个点更新其他点的距离
for (int j = 1; j <= n; j++)
{
// 1到j这个点的距离,和1到t再从t到j这个点的距离比大小
dist[j] = min(dist[j], dist[t] + g[t][j]);
}
}
// 1和n不联通
if (dist[n] == INF)
{
return -1;
}
else
{
return dist[n];
}
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g);
while (m--)
{
int a, b, c;
cin >> a >> b >> c;
// 处理重边,两条边只保留一条距离最短的边
g[a][b] = min(g[a][b], c);
}
int t = dijkstra();
cout << t;
return 0;
}
2、堆优化版的Dijkstra算法
时间复杂度:O(mlogn) 适合稀疏图(邻接表存) 边数=10^5
朴素版中,每次循环都要遍历dist数组,找到距离源点最近的点t
对优化版相比朴素版,用堆优化了 找到点t的操作
思路:
- 一号点的距离初始化为零,其他点初始化成无穷大。
- 将一号点放入堆中。
- 不断循环,直到堆空。每次循环中:
弹出堆顶,并标记该点的最短路径已经确定。
用该点更新其它点的距离,若更新成功就加入到堆中。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1000010;
const int INF = 0x3f3f3f3f;
int n, m;
// 节点的距离和编号
typedef pair<int, int> PII;
// 稀疏图,邻接表存图
int h[N], e[N], ne[N], idx;
int w[N];// 每条边的权重
// 表示每个点,当前为止,从1号点走到他自己的最短距离
int dist[N];
// 每个点的最短路是否已经确定
int st[N];
int dijkstra()
{
// 初始化距离
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
// 定义一个小根堆
priority_queue<PII, vector<PII>, greater<PII>> heap;
// 放入0号点,
// 0,1这个顺序不能倒,pair排序时是先根据first,再根据second
heap.push({ 0,1 });
while (heap.size())
{
// 取不在集合S中距离最短的点
PII t = heap.top();
heap.pop();
// ver节点编号
int ver = t.second;
int distance = t.first;
if (st[ver])
continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i])
{
// i只是个下标,e中在存的是i这个下标对应的点。
int j = e[i];
// 1号点到当前这个带的距离大于从1到t再到这个点的距离
if (dist[j] > distance + w[i])
{
// 更新j点的距离
dist[j] = distance + w[i];
// 把j这个点放到优先对列
heap.push({ dist[j],j });
}
}
}
// 1和n不联通
if (dist[n] == INF)
{
return -1;
}
else
{
return dist[n];
}
}
// 添加一条边 a指向b
// c是边的权重
void add(int a, int b, int c)
{
w[idx] = c;
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
while (m--)
{
int a, b, c;
cin >> a >> b >> c;
// 邻接表存图不需要对重边进行处理
add(a, b, c);
}
int t = dijkstra();
cout << t;
return 0;
}