leetcode-master/problems/kamacoder/0047.参会dijkstra朴素.md

945 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center"><strong><a href="./qita/join.md">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们受益!</strong></p>
# dijkstra朴素版精讲
[卡码网47. 参加科学大会](https://kamacoder.com/problempage.php?pid=1047)
【题目描述】
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。
小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。
小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。
【输入描述】
第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。
接下来为 M 行每行包括三个整数S、E 和 V代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。
【输出描述】
输出一个整数,代表小明从起点到终点所花费的最小时间。
输入示例
```
7 9
1 2 1
1 3 4
2 3 2
2 4 5
3 4 2
4 5 3
2 6 4
5 7 4
6 7 9
```
输出示例12
【提示信息】
能够到达的情况:
如下图所示,起始车站为 1 号车站,终点车站为 7 号车站,绿色路线为最短的路线,路线总长度为 12则输出 12。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227101345.png)
不能到达的情况:
如下图所示,当从起始车站不能到达终点车站时,则输出 -1。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227101401.png)
数据范围:
1 <= N <= 500;
1 <= M <= 5000;
## 思路
本题就是求最短路,最短路是图论中的经典问题即:给出一个有向图,一个起点,一个终点,问起点到终点的最短路径。
接下来,我们来详细讲解最短路算法中的 dijkstra 算法。
dijkstra算法在有权图权值非负数中求从起点到其他节点的最短路径算法。
需要注意两点:
* dijkstra 算法可以同时求 起点到所有节点的最短路径
* 权值不能为负数
(这两点后面我们会讲到)
如本题示例中的图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240125162647.png)
起点节点1到终点节点7 的最短路径是 图中 标记绿线的部分。
最短路径的权值为12。
其实 dijkstra 算法 和 我们之前讲解的prim算法思路非常接近如果大家认真学过[prim算法](./0053.寻宝-prim.md),那么理解 Dijkstra 算法会相对容易很多。这也是我要先讲prim再讲dijkstra的原因
dijkstra 算法 同样是贪心的思路,不断寻找距离 源点最近的没有访问过的节点。
这里我也给出 **dijkstra三部曲**
1. 第一步,选源点到哪个节点近且该节点未被访问过
2. 第二步,该最近节点被标记访问过
3. 第三步更新非访问节点到源点的距离即更新minDist数组
大家此时已经会发现这和prim算法 怎么这么像呢。
我在[prim算法](./0053.寻宝-prim.md)讲解中也给出了三部曲。 prim 和 dijkstra 确实很像,思路也是类似的,这一点我在后面还会详细来讲。
在dijkstra算法中同样有一个数组很重要起名为minDist。
**minDist数组 用来记录 每一个节点距离源点的最小距离**
理解这一点很重要,也是理解 dijkstra 算法的核心所在。
大家现在看着可能有点懵,不知道什么意思。
没关系,先让大家有一个印象,对理解后面讲解有帮助。
我们先来画图看一下 dijkstra 的工作过程,以本题示例为例: 以下为朴素版dijkstra的思路
**示例中节点编号是从1开始所以为了让大家看的不晕minDist数组下标我也从 1 开始计数下标0 就不使用了,这样 下标和节点标号就可以对应上了,避免大家搞混**
## 朴素版dijkstra
### 模拟过程
-----------
0、初始化
minDist数组数值初始化为int最大值。
这里在强点一下 **minDist数组的含义记录所有节点到源点的最短路径**,那么初始化的时候就应该初始为最大值,这样才能在后续出现最短路径的时候及时更新。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240130115306.png)
图中max 表示默认值节点0 不做处理统一从下标1 开始计算,这样下标和节点数值统一, 方便大家理解,避免搞混)
源点节点1 到自己的距离为0所以 minDist[1] = 0
此时所有节点都没有被访问过,所以 visited数组都为0
---------------
以下为dijkstra 三部曲
1、选源点到哪个节点近且该节点未被访问过
源点距离源点最近距离为0且未被访问。
2、该最近节点被标记访问过
标记源点访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240130115421.png)
更新 minDist数组源点节点1 到 节点2 和 节点3的距离。
* 源点到节点2的最短距离为1小于原minDist[2]的数值max更新minDist[2] = 1
* 源点到节点3的最短距离为4小于原minDist[3]的数值max更新minDist[3] = 4
可能有录友问:为啥和 minDist[2] 比较?
再强调一下 minDist[2] 的含义它表示源点到节点2的最短距离那么目前我们得到了 源点到节点2的最短距离为1小于默认值max所以更新。 minDist[3]的更新同理
-------------
1、选源点到哪个节点近且该节点未被访问过
未访问过的节点中源点到节点2距离最近选节点2
2、该最近节点被标记访问过
节点2被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240130121240.png)
更新 minDist数组源点节点1 到 节点6 、 节点3 和 节点4的距离。
**为什么更新这些节点呢? 怎么不更新其他节点呢**
因为 源点节点1通过 已经计算过的节点节点2 可以链接到的节点 有 节点3节点4和节点6.
更新 minDist数组
* 源点到节点6的最短距离为5小于原minDist[6]的数值max更新minDist[6] = 5
* 源点到节点3的最短距离为3小于原minDist[3]的数值4更新minDist[3] = 3
* 源点到节点4的最短距离为6小于原minDist[4]的数值max更新minDist[4] = 6
-------------------
1、选源点到哪个节点近且该节点未被访问过
未访问过的节点中,源点距离哪些节点最近,怎么算的?
其实就是看 minDist数组里的数值minDist 记录了 源点到所有节点的最近距离结合visited数组筛选出未访问的节点就好。
从 上面的图,或者 从minDist数组中我们都能看出 未访问过的节点中源点节点1到节点3距离最近。
2、该最近节点被标记访问过
节点3被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240130120434.png)
由于节点3的加入那么源点可以有新的路径链接到节点4 所以更新minDist数组
更新 minDist数组
* 源点到节点4的最短距离为5小于原minDist[4]的数值6更新minDist[4] = 5
------------------
1、选源点到哪个节点近且该节点未被访问过
距离源点最近且没有被访问过的节点有节点4 和 节点6距离源点距离都是 5 minDist[4] = 5minDist[6] = 5 ,选哪个节点都可以。
2、该最近节点被标记访问过
节点4被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201105335.png)
由于节点4的加入那么源点可以链接到节点5 所以更新minDist数组
* 源点到节点5的最短距离为8小于原minDist[5]的数值max更新minDist[5] = 8
--------------
1、选源点到哪个节点近且该节点未被访问过
距离源点最近且没有被访问过的节点是节点6距离源点距离是 5 minDist[6] = 5
2、该最近节点被标记访问过
节点6 被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201110250.png)
由于节点6的加入那么源点可以链接到节点7 所以 更新minDist数组
* 源点到节点7的最短距离为14小于原minDist[7]的数值max更新minDist[7] = 14
-------------------
1、选源点到哪个节点近且该节点未被访问过
距离源点最近且没有被访问过的节点是节点5距离源点距离是 8 minDist[5] = 8
2、该最近节点被标记访问过
节点5 被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201110651.png)
由于节点5的加入那么源点有新的路径可以链接到节点7 所以 更新minDist数组
* 源点到节点7的最短距离为12小于原minDist[7]的数值14更新minDist[7] = 12
-----------------
1、选源点到哪个节点近且该节点未被访问过
距离源点最近且没有被访问过的节点是节点7终点距离源点距离是 12 minDist[7] = 12
2、该最近节点被标记访问过
节点7 被标记访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201110920.png)
节点7加入但节点7到节点7的距离为0所以 不用更新minDist数组
--------------------
最后我们要求起点节点1 到终点 节点7的距离。
再来回顾一下minDist数组的含义记录 每一个节点距离源点的最小距离。
那么起到节点1到终点节点7的最短距离就是 minDist[7] 按上面举例讲解来说minDist[7] = 12节点1 到节点7的最短路径为 12。
路径如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201111352.png)
在上面的讲解中,每一步 我都是按照 dijkstra 三部曲来讲解的,理解了这三部曲,代码也就好懂的。
### 代码实现
本题代码如下,里面的 三部曲 我都做了注释,大家按照我上面的讲解 来看如下代码:
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int n, m, p1, p2, val;
cin >> n >> m;
vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
for(int i = 0; i < m; i++){
cin >> p1 >> p2 >> val;
grid[p1][p2] = val;
}
int start = 1;
int end = n;
// 存储从源点到每个节点的最短距离
std::vector<int> minDist(n + 1, INT_MAX);
// 记录顶点是否被访问过
std::vector<bool> visited(n + 1, false);
minDist[start] = 0; // 起始点到自身的距离为0
for (int i = 1; i <= n; i++) { // 遍历所有节点
int minVal = INT_MAX;
int cur = 1;
// 1、选距离源点最近且未访问过的节点
for (int v = 1; v <= n; ++v) {
if (!visited[v] && minDist[v] < minVal) {
minVal = minDist[v];
cur = v;
}
}
visited[cur] = true; // 2标记该节点已被访问
// 3第三步更新非访问节点到源点的距离即更新minDist数组
for (int v = 1; v <= n; v++) {
if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
minDist[v] = minDist[cur] + grid[cur][v];
}
}
}
if (minDist[end] == INT_MAX) cout << -1 << endl; // 不能到达终点
else cout << minDist[end] << endl; // 到达终点最短路径
}
```
* 时间复杂度O(n^2)
* 空间复杂度O(n^2)
### debug方法
写这种题目难免会有各种各样的问题,我们如何发现自己的代码是否有问题呢?
最好的方式就是打日志,本题的话,就是将 minDist 数组打印出来,就可以很明显发现 哪里出问题了。
每次选择节点后minDist数组的变化是否符合预期 ,是否和我上面讲的逻辑是对应的。
例如本题如果想debug的话打印日志可以这样写
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int n, m, p1, p2, val;
cin >> n >> m;
vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
for(int i = 0; i < m; i++){
cin >> p1 >> p2 >> val;
grid[p1][p2] = val;
}
int start = 1;
int end = n;
std::vector<int> minDist(n + 1, INT_MAX);
std::vector<bool> visited(n + 1, false);
minDist[start] = 0;
for (int i = 1; i <= n; i++) {
int minVal = INT_MAX;
int cur = 1;
for (int v = 1; v <= n; ++v) {
if (!visited[v] && minDist[v] < minVal) {
minVal = minDist[v];
cur = v;
}
}
visited[cur] = true;
for (int v = 1; v <= n; v++) {
if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
minDist[v] = minDist[cur] + grid[cur][v];
}
}
// 打印日志:
cout << "select:" << cur << endl;
for (int v = 1; v <= n; v++) cout << v << ":" << minDist[v] << " ";
cout << endl << endl;;
}
if (minDist[end] == INT_MAX) cout << -1 << endl;
else cout << minDist[end] << endl;
}
```
打印后的结果
```
select:1
1:0 2:1 3:4 4:2147483647 5:2147483647 6:2147483647 7:2147483647
select:2
1:0 2:1 3:3 4:6 5:2147483647 6:5 7:2147483647
select:3
1:0 2:1 3:3 4:5 5:2147483647 6:5 7:2147483647
select:4
1:0 2:1 3:3 4:5 5:8 6:5 7:2147483647
select:6
1:0 2:1 3:3 4:5 5:8 6:5 7:14
select:5
1:0 2:1 3:3 4:5 5:8 6:5 7:12
select:7
1:0 2:1 3:3 4:5 5:8 6:5 7:12
```
打印日志可以和上面我讲解的过程进行对比每一步的结果是完全对应的
所以如果大家如果代码有问题打日志来debug是最好的方法
### 如何求路径
如果题目要求把最短路的路径打印出来应该怎么办呢
这里还是有一些本题打印路径和 prim 打印路径是一样的我在 [prim算法精讲](./0053.寻宝-prim.md) 拓展 已经详细讲解了
在这里就不再赘述
打印路径只需要添加 几行代码 打印路径的代码我都加上的日志如下
```CPP
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int n, m, p1, p2, val;
cin >> n >> m;
vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));
for(int i = 0; i < m; i++){
cin >> p1 >> p2 >> val;
grid[p1][p2] = val;
}
int start = 1;
int end = n;
std::vector<int> minDist(n + 1, INT_MAX);
std::vector<bool> visited(n + 1, false);
minDist[start] = 0;
//加上初始化
vector<int> parent(n + 1, -1);
for (int i = 1; i <= n; i++) {
int minVal = INT_MAX;
int cur = 1;
for (int v = 1; v <= n; ++v) {
if (!visited[v] && minDist[v] < minVal) {
minVal = minDist[v];
cur = v;
}
}
visited[cur] = true;
for (int v = 1; v <= n; v++) {
if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
minDist[v] = minDist[cur] + grid[cur][v];
parent[v] = cur; // 记录边
}
}
}
// 输出最短情况
for (int i = 1; i <= n; i++) {
cout << parent[i] << "->" << i << endl;
}
}
```
打印结果:
```
-1->1
1->2
2->3
3->4
4->5
2->6
5->7
```
对应如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240201111352.png)
### 出现负数
如果图中边的权值为负数dijkstra 还合适吗?
看一下这个图: (有负权值)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227104334.png)
节点1 到 节点5 的最短路径 应该是 节点1 -> 节点2 -> 节点3 -> 节点4 -> 节点5
那我们来看dijkstra 求解的路径是什么样的继续dijkstra 三部曲来模拟 dijkstra模拟过程上面已经详细讲过以下只模拟重要过程例如如何初始化就省略讲解了
-----------
初始化:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227104801.png)
---------------
1、选源点到哪个节点近且该节点未被访问过
源点距离源点最近距离为0且未被访问。
2、该最近节点被标记访问过
标记源点访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227110217.png)
更新 minDist数组源点节点1 到 节点2 和 节点3的距离。
* 源点到节点2的最短距离为100小于原minDist[2]的数值max更新minDist[2] = 100
* 源点到节点3的最短距离为1小于原minDist[3]的数值max更新minDist[3] = 1
-------------------
1、选源点到哪个节点近且该节点未被访问过
源点距离节点3最近距离为1且未被访问。
2、该最近节点被标记访问过
标记节点3访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227110330.png)
由于节点3的加入那么源点可以有新的路径链接到节点4 所以更新minDist数组
* 源点到节点4的最短距离为2小于原minDist[4]的数值max更新minDist[4] = 2
--------------
1、选源点到哪个节点近且该节点未被访问过
源点距离节点4最近距离为2且未被访问。
2、该最近节点被标记访问过
标记节点4访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227110346.png)
由于节点4的加入那么源点可以有新的路径链接到节点5 所以更新minDist数组
* 源点到节点5的最短距离为3小于原minDist[5]的数值max更新minDist[5] = 5
------------
1、选源点到哪个节点近且该节点未被访问过
源点距离节点5最近距离为3且未被访问。
2、该最近节点被标记访问过
标记节点5访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227110405.png)
节点5的加入而节点5 没有链接其他节点, 所以不用更新minDist数组仅标记节点5被访问过了
------------
1、选源点到哪个节点近且该节点未被访问过
源点距离节点2最近距离为100且未被访问。
2、该最近节点被标记访问过
标记节点2访问过
3、更新非访问节点到源点的距离即更新minDist数组 ,如图:
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20240227110711.png)
--------------
至此dijkstra的模拟过程就结束了根据最后的minDist数组我们求 节点1 到 节点5 的最短路径的权值总和为 3路径 节点1 -> 节点3 -> 节点4 -> 节点5
通过以上的过程模拟,我们可以发现 之所以 没有走有负权值的最短路径 是因为 在 访问 节点 2 的时候,节点 3 已经访问过了,就不会再更新了。
那有录友可能会想: 我可以改代码逻辑啊,访问过的节点,也让它继续访问不就好了?
那么访问过的节点还能继续访问会不会有死循环的出现呢?控制逻辑不让其死循环?那特殊情况自己能都想清楚吗?(可以试试,实践出真知)
对于负权值的出现,大家可以针对某一个场景 不断去修改 dijkstra 的代码,**但最终会发现只是 拆了东墙补西墙**对dijkstra的补充逻辑只能满足某特定场景最短路求解。
对于求解带有负权值的最短路问题,可以使用 Bellman-Ford 算法 ,我在后序会详细讲解。
## dijkstra与prim算法的区别
> 这里再次提示,需要先看我的 [prim算法精讲](./0053.寻宝-prim.md) ,否则可能不知道我下面讲的是什么。
大家可以发现 dijkstra的代码看上去 怎么和 prim算法这么像呢。
其实代码大体不差,唯一区别在 三部曲中的 第三步: 更新minDist数组
因为**prim是求 非访问节点到最小生成树的最小距离,而 dijkstra是求 非访问节点到源点的最小距离**。
prim 更新 minDist数组的写法
```CPP
for (int j = 1; j <= v; j++) {
if (!isInTree[j] && grid[cur][j] < minDist[j]) {
minDist[j] = grid[cur][j];
}
}
```
因为 minDist表示 节点到最小生成树的最小距离,所以 新节点cur的加入只需要 使用 grid[cur][j] grid[cur][j] 就表示 cur 加入生成树后,生成树到 节点j 的距离。
dijkstra 更新 minDist数组的写法
```CPP
for (int v = 1; v <= n; v++) {
if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {
minDist[v] = minDist[cur] + grid[cur][v];
}
}
```
因为 minDist表示 节点到源点的最小距离,所以 新节点 cur 的加入,需要使用 源点到cur的距离 minDist[cur] + cur 到 节点 v 的距离 grid[cur][v]),才是 源点到节点v的距离。
此时大家可能不禁要想 prim算法 可以有负权值吗?
当然可以!
录友们可以自己思考思考一下,这是为什么?
这里我提示一下prim算法只需要将节点以最小权值和链接在一起不涉及到单一路径。
## 总结
本篇我们深入讲解的dijkstra算法详细模拟其工作的流程。
这里我给出了 **dijkstra 三部曲 来 帮助大家理解 该算法**,不至于 每次写 dijkstra 都是黑盒操作,没有框架没有章法。
在给出的代码中,我也按照三部曲的逻辑来给大家注释,只要理解这三部曲,即使 过段时间 对 dijkstra 算法有些遗忘,依然可以写出一个框架出来,然后再去调试细节。
对于图论算法一般代码都比较长很难写出代码直接可以提交通过都需要一个debug的过程所以 **学习如何debug 非常重要**
这也是我为什么 在本文中 单独用来讲解 debug方法。
本题求的是最短路径和是多少,**同时我们也要掌握 如何把最短路径打印出来**。
我还写了大篇幅来讲解 负权值的情况, 只有画图带大家一步一步去 看 出现负权值 dijkstra的求解过程才能帮助大家理解问题出在哪里。
如果我直接讲:是**因为访问过的节点 不能再访问,导致错过真正的最短路**,我相信大家都不知道我在说啥。
最后我还讲解了 dijkstra 和 prim 算法的 相同 与 不同之处, 我在图论的讲解安排中 先讲 prim算法 再讲 dijkstra 是有目的的, **理解这两个算法的相同与不同之处 有助于大家学习的更深入**。
而不是 学了 dijkstra 就只看 dijkstra 算法之间 都是有联系的,多去思考 算法之间的相互联系,会帮助大家思考的更深入,掌握的更彻底。
本篇写了这么长,我也只讲解了 朴素版dijkstra**关于 堆优化dijkstra我会在下一篇再来给大家详细讲解**。
加油
## 其他语言版本
### Java
```Java
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[][] grid = new int[n + 1][n + 1];
for (int i = 0; i <= n; i++) {
Arrays.fill(grid[i], Integer.MAX_VALUE);
}
for (int i = 0; i < m; i++) {
int p1 = scanner.nextInt();
int p2 = scanner.nextInt();
int val = scanner.nextInt();
grid[p1][p2] = val;
}
int start = 1;
int end = n;
// 存储从源点到每个节点的最短距离
int[] minDist = new int[n + 1];
Arrays.fill(minDist, Integer.MAX_VALUE);
// 记录顶点是否被访问过
boolean[] visited = new boolean[n + 1];
minDist[start] = 0; // 起始点到自身的距离为0
for (int i = 1; i <= n; i++) { // 遍历所有节点
int minVal = Integer.MAX_VALUE;
int cur = 1;
// 1、选距离源点最近且未访问过的节点
for (int v = 1; v <= n; ++v) {
if (!visited[v] && minDist[v] < minVal) {
minVal = minDist[v];
cur = v;
}
}
visited[cur] = true; // 2、标记该节点已被访问
// 3、第三步更新非访问节点到源点的距离即更新minDist数组
for (int v = 1; v <= n; v++) {
if (!visited[v] && grid[cur][v] != Integer.MAX_VALUE && minDist[cur] + grid[cur][v] < minDist[v]) {
minDist[v] = minDist[cur] + grid[cur][v];
}
}
}
if (minDist[end] == Integer.MAX_VALUE) {
System.out.println(-1); // 不能到达终点
} else {
System.out.println(minDist[end]); // 到达终点最短路径
}
}
}
```
### Python
```
import sys
def dijkstra(n, m, edges, start, end):
# 初始化邻接矩阵
grid = [[float('inf')] * (n + 1) for _ in range(n + 1)]
for p1, p2, val in edges:
grid[p1][p2] = val
# 初始化距离数组和访问数组
minDist = [float('inf')] * (n + 1)
visited = [False] * (n + 1)
minDist[start] = 0 # 起始点到自身的距离为0
for _ in range(1, n + 1): # 遍历所有节点
minVal = float('inf')
cur = -1
# 选择距离源点最近且未访问过的节点
for v in range(1, n + 1):
if not visited[v] and minDist[v] < minVal:
minVal = minDist[v]
cur = v
if cur == -1: # 如果找不到未访问过的节点,提前结束
break
visited[cur] = True # 标记该节点已被访问
# 更新未访问节点到源点的距离
for v in range(1, n + 1):
if not visited[v] and grid[cur][v] != float('inf') and minDist[cur] + grid[cur][v] < minDist[v]:
minDist[v] = minDist[cur] + grid[cur][v]
return -1 if minDist[end] == float('inf') else minDist[end]
if __name__ == "__main__":
input = sys.stdin.read
data = input().split()
n, m = int(data[0]), int(data[1])
edges = []
index = 2
for _ in range(m):
p1 = int(data[index])
p2 = int(data[index + 1])
val = int(data[index + 2])
edges.append((p1, p2, val))
index += 3
start = 1 # 起点
end = n # 终点
result = dijkstra(n, m, edges, start, end)
print(result)
```
### Go
### Rust
### JavaScript
```js
function dijkstra(grid, start, end) {
const visited = Array.from({length: end + 1}, () => false)
const minDist = Array.from({length: end + 1}, () => Number.MAX_VALUE)
minDist[start] = 0
for (let i = 1 ; i < end + 1 ; i++) {
let cur = -1
let tempMinDist = Number.MAX_VALUE
// 1. 找尋與起始點距離最近且未被訪的節點
for (let j = 1 ; j < end + 1 ; j++) {
if (!visited[j] && minDist[j] < tempMinDist) {
cur = j
tempMinDist = minDist[j]
}
}
if (cur === -1) break;
// 2. 更新節點狀態為已拜訪
visited[cur] = true
// 3. 更新未拜訪節點與起始點的最短距離
for (let j = 1 ; j < end + 1 ; j++) {
if(!visited[j] && grid[cur][j] != Number.MAX_VALUE
&& grid[cur][j] + minDist[cur] < minDist[j]
) {
minDist[j] = grid[cur][j] + minDist[cur]
}
}
}
return minDist[end] === Number.MAX_VALUE ? -1 : minDist[end]
}
async function main() {
// 輸入
const rl = require('readline').createInterface({ input: process.stdin })
const iter = rl[Symbol.asyncIterator]()
const readline = async () => (await iter.next()).value
const [n, m] = (await readline()).split(" ").map(Number)
const grid = Array.from({length: n + 1},
() => Array.from({length:n + 1}, () => Number.MAX_VALUE))
for (let i = 0 ; i < m ; i++) {
const [s, e, w] = (await readline()).split(" ").map(Number)
grid[s][e] = w
}
// dijkstra
const result = dijkstra(grid, 1, n)
// 輸出
console.log(result)
}
main()
```
### TypeScript
### PhP
### Swift
### Scala
### C#
### Dart
### C