图论之最短路径算法
Dijkstra 算法
迪杰斯特拉算法(Dijkstra)
是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。
是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。算法
主要特点
是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止
朴素Dijkstra算法
变量解释
变量名 | 含义 |
---|---|
int g[N][N] | g[i][j] i->j 的距离 |
int dis[N] | dis[i] 1->i 的最短距离 |
bool vis[N] | vis[i] 当前1->i 的最短距离是否确定 |
int n,m | n个点,m条边 |
算法流程
- 初始化最短距离。起点到自己的最短距离为0,到其他点的距离为
INF(0x3f3f3f3f)
- 找距离起点最近的点。在没有确定最短距离的点中,找到距离起点距离最近的一个点
- 标记。将2找到的最近的点标记为已经确定最短路径的点
- 松弛。用2找到的点,更新从起点到每一个点的最短距离,
min(dis[j], dis[t] + g[t][j])
- 重复第2步,直至所有存在最短路径的点更新完毕
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510;
int g[N][N]; // g[i][j] i->j 的距离
int dis[N]; // dis[i] 1->i 的最短距离
bool vis[N]; // vis[i] 当前1->i 的最短距离是否确定
int n,m;
int dijkstra() {
memset(dis, 0x3f, sizeof dis);
dis[1] = 0;
for(int i = 0; i < n; i++) {
// 访问 n 次
int t = -1;
for(int j = 1; j <= n; j++) {
// 访问 n 个点
if(!vis[j] && (t == -1 || dis[t] > dis[j]))
t = j;
}
vis[t] = true;
for(int j = 1; j <= n; j++) //依次更新每个点所到相邻的点路径值
dis[j] = min(dis[j], dis[t] + g[t][j]);
}
//如果第n个点路径为无穷大,不存在 1->n 最短路径
if(dis[n] == 0x3f3f3f3f) return -1;
return dis[n];
}
int main() {
cin >> n >> m;
memset(g, 0x3f, sizeof g);
for(int i = 0; i < m ; i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x][y] = min(g[x][y],z); //如果发生重边的情况则保留最短的一条边
}
cout << dijkstra() << endl;;
return 0;
}
堆优化版本
分析
为什么说是用堆进行优化,因为在算法的第一步中,需要在没有确定最短路径的点中,找到从起点到该点距离最短的一个点。
如果不加优化,这一步是O(n)
的时间复杂度,但是如果使用小根堆的话,就可以优化为O(1),随后的一个pop
操作,这是O(log n)
的。整体来看,第一步可以优化为O(log n)
随后,在进行松弛的过程中,最多有m条
边,而入队操作的时间复杂度为O(log n)
,总体时间复杂度为O(m log n)
#include <iostream>
#include <cstring>
#include <queue><