朴素版Dijkstra(最短路径)算法

一、图的知识框架

在这里插入图片描述
在这里插入图片描述

图的相关概念非常多,本篇只着重讲解下图所包含的内容。有了以下的内容作为基础,就已经能够学习Dijkstra算法。

在这里插入图片描述

二、基础知识

1、图的定义

关联一下之前学过的单链表和树,单链表是一对一的关系,树是一对多的关系,而图是多对多的关系,每个顶点都可以和其他多个顶点相关联。

图的严格定义:在这里插入图片描述

注:

  • 边也可以叫成 弧

  • 树可以是空树,但图不能为空,一个图至少要有一个点,可以没有边。

2、有向图 和 无向图 和 自环

图中所有边都没有方向的图是无向图
图中所有边都有方向的图是有向图

  • 无向图是特殊的有向图

3、无权图 和 带权图

  • 无权图可看作所有边权值都为1的带权图

4、稠密图 和 稀疏图

设一个图中:点数为n,边数为m

稠密图:m与n^2是一个级别

稀疏图:m与n是一个级别

如:题目给出测试点的范围是:1<=n<=500

              1<=m<=100000

这显然是一个稠密图。

5、邻接矩阵存图

讲了这么多概念,那一个图体现在代码里面是什么样子?邻接矩阵故名思意,用一个矩阵存图。

  • 对于一个有n个顶点的无权图,创建整型数组g[n][n],若第 i 个点到第 j 个点之间存在边,则令g[i-1][j-1]的值为1,反之,值为0。用0和1表示两点之间是否有边。
  • 对于有n个定点的带权图,同样使用g[n][n]来存储,若第 i 个点到第 j 个点之间存在权值为weight的边,则令g[i-1][j-1]的值为weight,若不存在边,则赋为 +∞
  • 邻接矩阵适合存储稠密图。因为矩阵的大小只与图的点数有关,与边数无关,只要点数固定,即使图再怎么复杂也能够用同一个矩阵存储,不需要耗费额外的内存空间。

三、Dijkstra(最短路径)算法

1、原理

Dijkstra算法是基于贪心的思想,每一步都找当前情况下的最优解,那么最后找出来的就是整体的最优解。Djkstra算法的严格证明不需要大家掌握,只要会用它解决问题即可。所以本文讲解的重点是Dijkstra算法的步骤。

2、朴素版Dijkstra算法的适用情况

  • 求单源最短路的情况,即任意一点到源点之间的最短路
  • 所有边权都是正数
  • 稠密图

3、算法步骤

以n个点的有向有权图为例:

  • 初始化所有点到源点的距离:开始只有源点的距离确定,为0,其他点的距离初始化为正无穷
  • 迭代n次
  • 每次确定一个点到起点的最短路。
    确定哪个点呢?是当前还没确定最短路径的点中,到起点距离最短的点。这个距离一定就是该点到源点的最短路,这里就是贪心的思想。
  • 确定这个点的最短距离之后,用这个点 来更新其他点到源点的最短距离。
    一次迭代结束。
  • 经过n次迭代,就可以确定n个点到源点的最短路。那么源点到终点的最短路也就确定了。

下面看一个例子:
在这里插入图片描述

4、代码实现

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。
请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。

输入格式

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

输出格式

输出一个整数,表示 1 号点到 n 号点的最短距离。
如果路径不存在,则输出 −1。

数据范围

1≤n≤500,
1≤m≤10^5,

输入样例:

3 3
1 2 2
2 3 1
1 3 4

输出样例:

3

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>

#define N 510
#define INF 0x3f3f3f3f
//这里涉及到无穷大为什么取0x3f3f3f3f的问题,可以查看下面的博客
//https://blog.csdn.net/m0_51551385/article/details/121522095

int n,m;//n为点数,m为边数
int g[N][N];//g为存储图的邻接矩阵
int dist[N];//dist[i]为点i到源点的距离
int st[N];//st[i]为1表示该点的最短距离已经确定,为0表示该点的最短距离还未确定

int dijkstra()
{
    memset( dist , 0x3f , sizeof(dist) );//初始化距离,对应Dijkstra算法的第一步
    dist[1] = 0;//源点到自己的最短距离为0

    for( int i = 0 ; i < n ; i++ )//对应第二步,进行n次迭代,每次确定一个点的最短路径
    {			   //用t表示当前选择的要确定最短路的点,t取值要求:不能是已经确定最短路的点
        int t = -1;//t = -1表示开始还没选择任何点
        for( int j = 1 ; j <= n ; j++ )//遍历所有点,找到符合条件的点
        {
            if( st[j] == 0 && (t == -1 || dist[t] > dist[j]) )//st[j] == 0确保t的最短路径还没有被确定
                t = j;					 //t == -1保证t 一定 是第一个遍历到的最短路没有确定的点
        }								 //dist[t] > dist[j]保证 t 是当前 到源点距离最短的点
        
		//t的最短距离已经确定,st数组标记为1
        st[t] = 1;
		
		//用t更新其他点到源点的最短距离
        for( int j = 1 ; j <= n ; j++ )
        {
            dist[j] = fmin( dist[j] , dist[t] + g[t][j] );
        }
    }

    if( dist[n] == INF )
        return -1;
    else
        return dist[n];
}

int main()
{
    scanf("%d %d",&n,&m);//输入点数和边数

    memset( g , 0x3f , sizeof(g) );//初始化邻接矩阵

    for( int i = 1 ; i <= n ; i++ )//每个点到自己距离为0
    {							   //因为输入的点的编号从 1 开始,所以为了后续操作方便,第0行 和 第0列舍弃,直接从第一行第一列开始存
        g[i][i] = 0;
    }

    while( m-- )//输入m条边
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        g[a][b] = fmin( g[a][b] , c);//可能有重边,所以只保留距离最短的边即可
        //g[b][a] = g[a][b];如果是无向图的话采用这种方式来建图
    }

    int t = dijkstra();

    if( t == -1 )
        printf("-1");
    else
        printf("%d",t);

    return 0;
}

5、时间复杂度:O(n^2)

四、作业

利用最短路径Dijkstra算法解决实际问题,校园中的最短路问题。
在这里插入图片描述

说明:

  • 起点(源点)和终点的选取是随意的,只要能用Dijkstra算法输出他们的最短路即可。
  • 不要求手动输入,直接在main函数中建图即可。

五、拓展

下图中列举了所有求最短路径的情况,有兴趣的可以自行学习。
在这里插入图片描述

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Dijkstra最短路径算法是一种带权图或树的单源最短路径算法,它的主要思想是在访问过的顶点中,找到距离源点最近的顶点,然后以该顶点为中介点,更新其他顶点的最短路径。 Java实现Dijkstra最短路径算法的一种方法是: 1. 创建一个最短路径数组dist[],用来存储每个顶点到源点的最短距离。 2. 创建一个visited[]数组,用来存储每个顶点是否已经访问过。 3. 初始化源点的最短路径为0,其他顶点的最短路径为无穷大。 4. 在未访问的顶点中找到最短路径的顶点u。 5. 标记顶点u为已访问过。 6. 更新从顶点u出发到其他顶点v的最短路径。 7. 重复步骤4-6,直到所有顶点都被访问过。 8. 输出最短路径数组dist[]。 这是一个简单的实现方法,也可以使用优先队列优化算法复杂度。 ### 回答2: Dijkstra最短路径算法是一种常见的求解图中最短路径算法,它可以用来解决许多现实生活中的问题,比如求地图中两点之间的最短路程或者求邮递员最优路径等。 Java中实现Dijkstra算法需要以下步骤: 1. 定义图节点类 定义一个GraphNode类,其中包含节点编号、距离和一个HashMap存储与当前节点相邻的其他节点。 2. 编写Dijkstra算法 利用PriorityQueue和HashSet数据结构,实现Dijkstra算法,并返回从起始节点到各个终止节点的最短路径。具体实现过程如下: a. 将起始节点的距离设为0,其他节点的距离设为无穷大。 b. 将所有节点添加到PriorityQueue中,按照距离升序排序。 c. 不断从PriorityQueue中取出距离最小的节点,将其加入到HashSet中,更新所有与该节点相邻的节点的距离。 d. 重复上述步骤,直到PriorityQueue为空。 3. 测试 定义一个测试类,通过输入图的节点、边和权重信息,构建出图并测试Dijkstra算法的正确性。 在实现Dijkstra算法时,需要注意以下几点: 1. 若图中存在负权边,则Dijkstra算法不能正确求解最短路径,可以采用Bellman-Ford算法解决。 2. 由于Java中PriorityQueue根据元素自然顺序进行排序,因此需要重写GraphNode类的比较方法,使其按照节点距离进行排序。 3. 一般情况下,使用HashMap存储GraphNode类与其他节点的连接关系可以较快地查找到与当前节点相邻的其他节点。 总之,Dijkstra最短路径算法是一种优秀的图算法,Java中实现也非常简单,只需要通过PriorityQueue和HashSet等数据结构实现核心算法即可。在实际应用中,我们可以根据不同场景选择不同的算法算法改进来满足实际需求。 ### 回答3: Dijkstra最短路径算法是一种经典的图论算法,用于在一个带权有向图中,从一个源点出发,计算出到其他所有点的最短路径。该算法采用贪心策略,每次选择当前未确定最短路径的节点中,距离源点最近的节点作为下一个确定的节点,直到所有节点都被确定为止。 在Java中,可以使用邻接矩阵或邻接表存储图的结构。在使用邻接矩阵存储图时,可以采用二维数组存储图中每个节点之间的距离。在使用邻接表存储图时,可以采用一个哈希表存储每个节点及其相邻的节点和边的信息。具体实现时,可以定义一个节点类和一个边类,每个节点类包含节点编号、到源点的距离和一个布尔值表示是否已经确定最短路径,每个边类包含起点、终点和权值。 Dijkstra算法可以用一个优先队列来存储未确定最短路径的节点,每次取出距离源点最近的节点进行更新,同时将与其相邻的节点加入队列中。具体实现时,可以定义一个dist数组存储每个节点到源点的距离,一个parent数组存储每个节点在最短路径中的前驱节点,一个优先队列来存储未确定最短路径的节点,以及一个visited数组表示每个节点是否已经被访问过。 具体算法步骤如下: 1. 初始化dist数组和visited数组,将源点的距离设为0,将源点加入优先队列中 2. 从优先队列中取出距离源点最近的节点,将其标记为已访问 3. 遍历该节点相邻的所有未访问过的节点,如果通过该节点可以更新距离,则更新dist数组和parent数组,并将节点加入优先队列中 4. 重复步骤2和3,直到所有节点都被访问过 最后,可以通过遍历parent数组来获取从源点到其他节点的最短路径。总的时间复杂度为O(ElogV),其中E为边数,V为节点数,由于使用了优先队列,因此算法的时间复杂度与边数相关,适合稠密图和稀疏图的计算。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值