Dijkstra算法求最短路

Dijkstra算法可以在图中寻找一个节点(称为“源节点”)到所有其它节点的最短路径。

Dijkstra算法可以在图中寻找一个节点(称为“源节点”)到所有其它节点的最短路径。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Dijkstra算法是什么?

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

Dijkstra算法主要解决单源最短路径问题。

注:Dijkstra算法无法解决寻找当有权图中权值为负数的最短路。

二、问题介绍

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

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

输入格式:

第一行包含整数 n和 m。

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

输出格式:

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

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

数据范围:

1≤n≤500
1≤m≤10^5
图中涉及边长均不超过10000。

三、朴素版Dijkstra算法

1.图的存储

无向图是特殊的有向图,故用Dijkstra算法处理问题我们只用考虑有向图即可。

朴素版Dijkstra算法常用于解决稠密图即图的边数比较多的情况,稠密图用邻接矩阵来存储,稀疏图通常用邻接表存储。

邻接矩阵存储图,我们通过一个二维数组map,行数和列数都对应点的个数,当1结点有一条指向2结点的边,且权值是5是,那么我们就设置map[1][5] = 5即可。

图1.1 图用邻接矩阵存储模拟

2.算法实现

 图2.1思路模拟

这个题存在重边和自环,因为我们是求最短路,那么自环就不用考虑,重边我们存储权值小的边即可。

我们引入邻接矩阵map来存储地图,初始化当i=j的时候初始化为0,然后有重边的话要最小的值,其余的初始化为0x3f3f方便后续dist数组中比较最小值,同时引入n表示结点的个数,m表示边数,一维数组dist表示起点1到第i个点的最小距离。对dist数组进行初始化,dist[1]=0即第一个顶点自身不需要走。然后剩余值初始化为0x3f3f。

注:0x3f3f是16383,超过10^5次方。

同时引入一维数组flag,如果这个点被找到了最短距离,设置为true。

接下来我们遍历n次,即n个点,第一次遍历然后找到在未确定找到最小距离的点中找到离第一个点最近的点,本次找到的肯定是1这个点本身,然后用1这个点初始化dist数组,那么此时dist数组内部的值全变成从1这个点直接到各个点的最短距离;第二次循环,我们找到离1这个结点最短距离的点假设是结点t(因为从第一轮循环1结点肯定是1结点本身离得最近的点,它会被flag设置为tue,我们后续内层循环遍历的时候就不需要再判断这个点),然后用结点t到各个点的直接距离+地图上的权值(即dp[t]+map[t][j])和原本dist数组1结点到各个结点的最短距离(dist[j])比较取最小值,此时我们就得到了现在这个状态下1结点到各个结点的最短距离。后续重复以上说的过程。注:中间如果没有路径到达这个点,那么dist数组中这个点的值会是0x3f3f。当我们进行第n轮循环,那么此时就是到离第n个结点最近的节点,一定是它本身即t = n(只要出现t=n我们就可以退出循环了,因为此时相当于我们已经找到最短路径了就是此时的dist[n]),那么我们在初始化dist数组之后,dist[n]就是我们从1到n的最短距离。如果我们没有路径到达终点那么当前点会被标记为访问过,会循环找别的离上一个点最近的点。当dist[n]=0x3f3f,说明没有路径能到达最后一个结点,输出-1即可。

注 n >= j >= 1

我们可以模拟一下上述的文字描述的循环过程:

 图2.2dists数组初始化

图2.3第一轮dist数组 

 图2.4第二轮dist数组

图2.4第三轮dist数组 

图2.5第四轮dist 数组

通过dist的值变化我们就发现,当我们找到离上一个结点最近的结点t为n时,就可以直接退出循环了,因为后面dist数组的值就不会再发生变化了。

四、使用步骤

1.代码如下(示例):


import java.io.*;
import java.util.*;
public class 朴素版Dijkstra算法 {
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    //邻接矩阵存储图
    static int[][] map;
    //存储点的个数
    static int n;
    //存储边的条数
    static int m;
    //从1号点走到第i个点的距离是多少
    static int[] dist;

    //表示第i个结点的最短距离是否确定
    static boolean[] flag;
    public static void main(String[] args) {
        Scanner sc = new Scanner(br);
        n = sc.nextInt();
        m = sc.nextInt();
        map = new int[510][510];
        flag = new boolean[510];
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= n;j++){
                map[i][j] = 0x3f3f;
                if(i == j){
                    map[i][j] = 0;
                }
            }
        }
        while (m-- > 0){
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            map[a][b] = Math.min(map[a][b],c);
        }
        int result = dijkstra();
        pw.println(result);
        pw.flush();
    }
    public static int dijkstra(){
        //初始化所有的距离
        dist = new int[510];
        dist[1] = 0;
        for(int i = 2;i <= n;i++){
            dist[i] = 0x3f3f;
        }
        //循环n次
        for(int i = 0;i < n;i++){
            int t = -1;
            for(int j = 1;j <= n;j++){
                //在未确定最短距离的点中找出上一个已确定最短距离点的距离最小的点
                if(!flag[j] && (t == -1 || dist[t] > dist[j])){
                    t = j;
                }
            }
            flag[t] = true;
            //用最短距离t点来更新dist数组中的值
            for (int j = 1;j <= n;j++){
                dist[j] = Math.min(dist[j],dist[t]+map[t][j]);
            }
        }
        if(dist[n] == 0x3f3f){
            return -1;
        }
        return dist[n];
    }
}

2.读入数据

3 3
1 2 2
2 3 1
1 3 4

3.代码运行结果

3

1->2 权值2 2->3权值1

2+1 = 3


总结

代码可能不太好理解,debug几次看一下。

  • 47
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值