Bootstrap

Dijkstra求最短路(一) 朴素版本-算法基础-数据结构(二)

题目样例

题目描述

给定一个包含 n 个点和 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。

请你求出从 1 号点到 n 号点的最短距离。如果无法从 1 号点走到 n 号点,则输出 −1。


输入格式

第一行包含两个整数 nm,分别表示点的数量和边的数量。

接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z


输出格式

输出一个整数,表示从 1 号点到 n 号点的最短距离。

如果路径不存在,则输出 −1。


数据范围

  • 1≤n≤500
  • 1≤m≤105
  • 图中涉及边长均不超过 10000

输入样例

3 3
1 2 2
2 3 1
1 3 4

输出样例

3

代码实现

#include<iostream>
using namespace std;
const int N = 510;
int g[N][N]; //用于存储两个点的距离
int dist[N]; //用于存储每个点最短距离
bool st[N];  //由于确定每个点是否已经确定最短距离
int n, m;
int disjkstra()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0; //第一个点到第一个点的距离为0
 
	for (int i = 0; i <= n; i++)
	{
		int k = -1;
		for (int j = 1; j <= n; j++)
		{
			if (!st[j] && (k == -1 || dist[k] > dist[j])) //如果当前点还没有被确定为最短距离就遍历找到那个最小距离
			{
				k = j;  //更新k的位置,使k在当前遍历的点中处于最小距离的那个点的位置
			}
			
		}
		st[k] = true; //此时就已经确定了k这个点的最小距离
 
		for (int j = 1; j <= n; j++)
		{
			dist[j] = min(dist[j], dist[k] + g[k][j]); //将j所处位置的最小值更新一遍,如a到c的距离为5,
													   //a到b到c的距离为3,那么此时就应该更新为3
		}
	}
	if (dist[n] == 0x3f3f3f3f)return -1;
	return dist[n];
}
int main()
{
	memset(g, 0x3f, sizeof g); //初始化,先使每个点距离都为正无穷
 
	cin >> n >> m;
	while (m--)
	{
		int a, b, c;
		cin >> a >> b >> c;        //表示点a到点b的距离为c
		g[a][b] = min(g[a][b], c); //如果有重边的情况下只要存储最短的一条边就可以了
	}
	int t = disjkstra();
	cout << t << endl;
	return  0;
}

代码思路总结

1. 问题分析
  • 给定一个包含 n 个点和 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
  • 要求从 1 号点到 n 号点的最短距离。如果无法到达,则输出 −1。
  • 这是一个典型的最短路径问题,适合使用 Dijkstra 算法 解决。

2. 数据结构设计
  • 邻接矩阵 g[N][N]
    • 用于存储图中两个点之间的距离。
    • 初始化时,所有距离设置为无穷大(0x3f3f3f3f),表示不可达。
    • 如果存在重边,只保留最短的一条边。
  • 距离数组 dist[N]
    • 用于存储从起点 11 到每个点的最短距离。
    • 初始化时,dist[1] = 0,其余点的距离为无穷大。
  • 标记数组 st[N]
    • 用于标记每个点是否已经确定了最短距离。
    • 初始时,所有点均未确定最短距离。

3. Dijkstra 算法实现
  • 初始化

    • dist 数组初始化为无穷大,dist[1] = 0
    • st 数组初始化为 false,表示所有点均未确定最短距离。
  • 主循环

    • 遍历所有点,找到当前未确定最短距离且距离起点最近的点 kk

    • 将点 k 标记为已确定最短距离(st[k] = true)。

    • 遍历点 k 的所有邻接点 j,更新 j 的最短距离:

      dist[j] = min(dist[j], dist[k] + g[k][j]);
      
    • 重复上述过程,直到所有点的最短距离确定。

  • 结果判断

    • 如果 dist[n] 仍然是无穷大,说明无法从起点到达终点,输出 −1。
    • 否则,输出 dist[n]

4. 代码实现细节
  • 邻接矩阵的初始化

    • 使用 memset(g, 0x3f, sizeof g) 将所有距离初始化为无穷大。

    • 在输入边时,如果有重边,只保留最短的一条边:

      g[a][b] = min(g[a][b], c);
      
  • Dijkstra 算法的优化

    • 使用朴素 Dijkstra 算法,时间复杂度为 O(n2)O(n2),适合 n≤500n≤500 的数据范围。
    • 如果 n 较大(如 n*≤105),需要使用堆优化的 Dijkstra 算法,时间复杂度为 O(mlog⁡n)。

5. 时间复杂度分析
  • 朴素 Dijkstra 算法
    • 每次找到未确定最短距离且距离起点最近的点 k,需要遍历所有点,时间复杂度为 O(n)。
    • 更新邻接点的距离,时间复杂度为 O*(n)。
    • 总时间复杂度为 O(n2)。
  • 堆优化 Dijkstra 算法
    • 使用优先队列(堆)维护未确定最短距离的点,时间复杂度为 O(mlogn)。

6. 代码总结
  • 优点
    • 代码结构清晰,逻辑简单,适合初学者理解 Dijkstra 算法的基本思想。
    • 使用邻接矩阵存储图,适合稠密图(边数接近 n2)的情况。
  • 缺点
    • 对于稀疏图(边数远小于 n2),邻接矩阵会浪费大量空间。
    • 朴素 Dijkstra 算法的时间复杂度较高,不适合 n 较大的情况。

算法刷题记录

;