Dijkstra算法
Dijkstra算法
Dijkstra算法是基于贪心思想实现的,它的基本步骤为:
- 初始化距离,dist[1] = 0,dist[i] = ∞,代表着只有源点的最短路径确定了,就是0,其余的点的最短路径皆未确定,都是∞
- 定义一个集合S,该集合装入的点是已经确定最短路径的点
- 循环迭代,每次:
- 找到不在S中的,离源点距离最短的点t
- 把t点加入S中
- 用t来更新剩下的点到源点的距离
时间复杂度分析:首先我们要通过一个大循环来把每个点都确定最短路径,时间复杂度为O(n),其次我们每次还要通过循环找到未确定最短路的距离源点距离最小的点,时间复杂度为O(n),然后我们还要以此点去更新剩下所有的点距源点的距离,时间复杂度为O(n^2)
这就是Dijkstra算法的思路,下图是一个例子,可以看下循环的过程:
具体的代码实现如下:
//给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值。
//请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
// 1≤n≤500,1≤m≤105
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 510;
//dist[N]从一号点走到每个点的当前的最短距离是多少
//st[N]是是否已经确认了是最短路径点
int l[N][N],dist[N],st[N];
int n,m;
int dijkstra() {
//距离初始化为正无穷
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
for (int i = 0;i < n; i++) {
//先找到没有确定最短路长度的点中dist最小的那一个
int t = -1;
for (int j = 1;j <= n; j++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
}
st[t] = 1;
//拿t来更新其他点的距离
for (int j = 1;j <= n; j++) {
dist[j] = min(dist[j],dist[t] + l[t][j]);
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main() {
scanf("%d %d",&n,&m);
//初始化:
memset(l,0x3f,sizeof l);
while (m--) {
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
l[a][b] = min(l[a][b],c);
}
int res = dijkstra();
cout << res <<endl;
return 0;
}
堆优化版的Dijkstra算法
Dijkstra算法中,我们需要从头到尾扫描一遍去找最小的点,这一步操作在数据量较大时会TLE,因而我们可以采用堆这个结构来维护每个点到源点的距离,堆顶元素就是最小的点。
可以知道,在堆中找到最小的元素的时间复杂度为O(1),修改一个数则是O(logn),假设我们有m个点,则总共的修改次数为O(mlogn)的。
具体的思路和Dijkstra算法一样,只是我们需要采用堆来优化,C++中我们可以使用STL里的优先队列priority_queue,具体的代码实现如下:
//给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为非负值。
//请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1。
//1≤n,m≤1.5×10^5
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int,int> P;
const int N = 100010;
vector<vector<P>> map;
int dist[N];
bool st[N];
int n,m;
int Dijkstra() {
memset(dist,0x3f,sizeof dist);
dist[1] = 0;
//小根堆的定义方式
priority_queue<P,vector<P>,greater<P>> heap;
//编号1,距离0,放入起点
heap.push({0,1});
while (heap.size()) {
auto t = heap.top();
heap.pop();
//node点的编号,distance到源点距离
int node = t.second;
int distance = t.first;
//如果node已经被更新过了,就没必要处理了
if (st[node]) continue;
//标记该点已经确定了最短路
st[node] = true;
//用该点更新剩下点的距离,如果更新了要把更新成功的权值放进堆里去
for (int i = 0; i < map[node].size(); i++) {
int newnode = map[node][i].first;
int len = map[node][i].second;
if (dist[newnode] > dist[node] + len) {
dist[newnode] = dist[node] + len;
heap.push({dist[newnode],newnode});
st[newnode] = true;
}
}
}
if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];
}
int main() {
cin >> n >> m;
map.resize(n+1);
while (m--) {
int a,b,c;
cin >> a >> b >> c;
map[a].push_back({b,c});
}
cout << Dijkstra() << endl;
return 0;
}