
<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] = 5，minDist[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

