描述:
网上已经有很多对于该算法的详尽描述了,本文不做赘述。
本文的目的是提供给大家一个带有清晰注释的使用C++实现Dijkstra算法的代码。
源代码:
//
// main.cpp
// DijkstraAlgorithm
//
// Created by 胡昱 on 2021/12/21.
//
#include <iostream>
#include <cstring>
#include <limits.h>
using namespace std;
// 定义相关参数
const int MAX_N = 500; // 最大顶点数
int graph[MAX_N + 1][MAX_N + 1]; // 以二维数组graph表示图,注意点的编号是从1开始的
int dis[MAX_N + 1]; // 从起点s到其他点的距离数组dis
bool mark[MAX_N + 1]; // 记录某点是否已被访问的数组
int n, E, s, t; // 顶点数n、边数E、起点s以及终点t
// 主函数
int main(int argc, const char * argv[]) {
// 共T组测试用例
int T;
cin >> T;
while((T--) > 0) {
// 初始化,注意memset(..., 127, ...)是将数组中的所有元素全为2139062143,即无穷大
memset(graph, 127, sizeof(graph));
memset(dis, 127, sizeof(dis));
memset(mark, false, sizeof(mark));
// 输入顶点数n、边数E、起点s以及终点t
cin >> n >> E >> s >> t;
// 顶点u到顶点v之间无向边长度w
// 因为可能有重边,所以需要取最小者
// 虽然是无向图,但是我们改成有向图来处理
for(int ei = 0; ei < E; ++ei) {
int u, v, w;
cin >> u >> v >> w;
graph[u][v] = min(graph[u][v], w);
graph[v][u] = min(graph[v][u], w);
}
// 开始Dijkstra算法
// 起点到起点的距离应该为0
dis[s] = 0;
// 外层大循环保证所有点都会被访问到
// 在输入的时候并没有更新dis数组,因为很明显第一个被访问的就是起点s(距离dis[s]为0)
for(int ni = 1; ni <= n; ++ni) {
// 记录当前访问的点的变量
int currentDis = INT_MAX;
int currentVex;
// 在未被访问过的点中寻找当前需要访问的点
// 也就是未被访问过的、和起点距离最短的点
for(int cvi = 1; cvi <= n; ++cvi) {
if((!mark[cvi]) && (dis[cvi] < currentDis)) {
currentDis = dis[cvi];
currentVex = cvi;
}
}
// 将该点设置成已访问状态
mark[currentVex] = true;
// 更新所有和当前点连接的点的距离
/*
问:这边为什么不用考虑更新已经访问过的点的距离呢?
答:因为已访问的点到起点s的距离一定小于当前点到起点s的距离,
所以从起点s到当前点再到已访问的点的距离一定大于从起点s到已访问的点的距离
*/
for(int i = 1; i <= n; ++i) {
// 如果点i未被访问、且从起点s到当前点currentVex再到点i的距离小于现有的从起点s到点i的距离的话,则更新
if((!mark[i]) && (dis[currentVex] + graph[currentVex][i] < dis[i])) {
dis[i] = dis[currentVex] + graph[currentVex][i];
}
}
}
// Dijkstra算法结束,从代码中的两层for循环可以很明显看出时间复杂度为O(n^2)
// 输出结果(如果从起点s无法到达终点t则输出-1)
if(dis[t] == INT_MAX) {
cout << -1 << endl;
}
else {
cout << dis[t] << endl;
}
}
return 0;
}