假期过长,导致停更了好长时间,复习一道算法题找找感觉。

前段时间看到一篇文章,里面提到了统治世界的十大算法,其中之一就是迪杰斯特拉算法(Dijkstra),该算法主要解决的”最短路径“这一类问题。说法虽然夸张了点,但它在实际生活中确实应用广泛,例如地图软件等,大部分游戏中自动寻路等功能,使用到的 A*搜索算法也是基于迪杰斯特拉算法优化而来。那么迪杰斯特拉算法是如何实现的呢?

假如我们现在有如下一个有向图,图中有 6 个顶点,编号分别为 1~6,带有箭头的直线表示的是能从一个顶点到达另外一个顶点,直线上的数字表示的是两个顶点之间的距离,现在求顶点 1 到顶点 6 的最短距离。

由于图中的点比较少,我们直接手动计算就能算出来这个结果,但是如果顶点很多,有成千上万个,手动计算就很难了,我们只能通过程序来计算,那么我们的程序该如何写呢?

思路

从图中我们可以看到,顶点 1 只能直接到达顶点 2、3、5,不能直接到达顶点 6,所以要想从 1 到达 6,就必须得从其他顶点中转。

我们定义一个数组,用来表示每个顶点到顶点 1 的距离,数组的索引表示的是顶点编号,数组元素的值表示的是到顶点 1 的最小距离。

  1. 开始我们在顶点 1 上,顶点 1 能到达 2、3、5,数组的状态如下。

    索引 1 2 3 4 5 6
    最小距离 0 60 10 null 50 null
  2. 从顶点 2 处中转,顶点 2 能到达顶点 4,距离为 35,所以顶点 1 到顶点 4 的距离为 60+35=95,数组状态如下:

索引 1 2 3 4 5 6
最小距离 0 60 10 95 50 null
  1. 从顶点 3 处中转,顶点 3 能到达 4,距离为 30。如果通过顶点 3 中转,那么 1 到达 4 的距离为 40,小于经过 2 中转的距离,所以数组中到顶点 4 的距离更新为 40。顶点 3 还能到达顶点 5,同理,通过顶点 2 中转,1 到达 5 的距离为 10+25=35,小于 1 直接到达 5 的距离,因此数组中到达顶点 5 的距离更新为 35,更新后,数组状态如下:
索引 1 2 3 4 5 6
最小距离 0 60 10 40 35 null
  1. 从顶点 5 中转,顶点 5 能到达 2,如果顶点 1 通过顶点 5 到达 2,距离为 35+30=65,大于顶点 1 直接到达 2,所以不更新。由于顶点 2 在前面已经遍历过它能到达哪些点了,所以后面不需要再遍历它。顶点 5 还能到达顶点 6,所以此时 1 到达 6 的距离为 35+105=140,数组状态如下:
索引 1 2 3 4 5 6
最小距离 0 60 10 40 35 140
  1. 从顶点 4 中转,顶点 4 也能达到顶点 6。如果通过顶点 4 中转,那么 1 到达 6 的距离为 40+15=55,这个距离小于从 5 中转,所以更新数组,更新后,数组状态如下:
索引 1 2 3 4 5 6
最小距离 0 60 10 40 35 55

以上流程,就是迪杰斯特拉算法在计算最短路径问题时的核心流程。

代码实现

首先我们需要将这个有向图用代码表示出来,通常表示图的方法有两种:第一种是邻接矩阵(也就是二维数组),第二种是邻接表(也就是数组+链表),这里我们选用邻接表法来表示一个有向图。

另外我们还需要定义顶点之间的边,一条边有起点和终点,还有边的权重信息,也就是长度,用类 Edge 表示。代码如下:

private class Edge {    // 起始定点    public int s;    // 终止定点    public int t;    // 边的权重    public int weight;

    Edge(int s, int t, int weight) {        this.s = s;        this.t = t;        this.weight = weight;    }}

有向图我们用类 Graph 表示,类中有两个属性:顶点个数 v 和描述顶点之间边的信息的数组 adj,我们还提供了一个方法:addEdge,用来在两个顶点之间添加一条边。代码如下:

public class Graph {

    // 顶点个数(顶点编号从0开始,在本文例子中,编号为0的顶点不存在)    private int v;

    // 记录每个顶点的边    private LinkedList[] adj;public Graph(int v) {this.v = v;// 初始化this.adj = new LinkedList[v];for (int i = 0; i             adj[i] = new LinkedList();        }    }// 添加一条边,从s到达tpublic void addEdge(int s, int t, int weight) {        Edge edge = new Edge(s, t, weight);        adj[s].add(edge);    }}

定义好了图的描述信息后,接下来通过代码来实现迪杰斯特拉算法,其代码和注释如下。

有两处逻辑稍微解释一下。第一处:flag 数组记录的是已经遍历过的顶点,用来防止死循环,例如顶点 1 能到达 2,我们接着会判断 2 能到达哪些点,顶点 1 又能到达 5,5 也能到达 2,如果没有 flag 数组来记录顶点 2 我们已经遍历过了,那么我们就会继续遍历 2,这样会导致死循环。第二处:predecessor 数组记录的是路径信息,数组的索引表示的顶点编号,元素的值表示的是哪一个顶点到达当前顶点的,例如:predecessor[3]=1 表示的是通过顶点 1 到达的顶点 3。

// 采用迪杰斯特拉算法找出从s到t的最短路径public void dijkstra(int s, int t) {    int[] dist = new int[v];    // 记录s到每个顶点的最小距离,数组下标表示顶点编号,值表示最小距离    boolean[] flag = new boolean[v];    // 记录遍历过的顶点,数组下标表示顶点编号,值表示是否遍历过该顶点    for (int i = 0; i         dist[i] = Integer.MAX_VALUE;    // 初始状态下,将顶点s到其他顶点的距离都设置为无穷大    }    int[] predecessor = new int[v];     // 记录路径,索引表示顶点编号,值表示到达当前顶点的顶点是哪一个    Queue queue = new LinkedList<>();    queue.add(s);    dist[s] = 0;    // s->s的路径为0while (!queue.isEmpty()) {        Integer vertex = queue.poll();if (flag[vertex]) continue; // 已经遍历过该顶点,就不再遍历        flag[vertex] = true;for (int i = 0; i             Edge edge = adj[vertex].get(i);if (dist[vertex] // 如果出现了比当前路径小的方式,就更新为更小路径                dist[edge.t] = dist[vertex] + edge.weight;                predecessor[edge.t] = vertex;            }            queue.add(edge.t);        }    }// 打印路径    System.out.println("最短距离:" + dist[t]);    System.out.print(s);    print(s, t, predecessor);}

print 函数的作用是打印从顶点 s 到达顶点 t 的最短路径中,需要经过哪些点,具体代码如下,就是一个递归调用,比较简单:

// 打印路径private void print(int s, int t, int[] predecessor) {    if (t == s) {        return;    }    print(s, predecessor[t], predecessor);    System.out.print(" -> " + t);}

测试

根据文中的示例,构建一个图,进行结果测试,代码如下:

public static void main(String[] args) {    // 构建图    Graph graph = new Graph(7);    graph.addEdge(1, 2, 60);    graph.addEdge(1, 3, 10);    graph.addEdge(1, 5, 50);    graph.addEdge(2, 4, 35);    graph.addEdge(3, 4, 30);    graph.addEdge(3, 5, 25);    graph.addEdge(4, 6, 15);    graph.addEdge(5, 2, 30);    graph.addEdge(5, 6, 105);    // 计算最短距离    graph.dijkstra(1, 6);}

测试结果:

总结

迪杰斯特拉算法的思想与广度优先搜索(BFS)的思路比较像,每次找到自己能到达的顶点,然后依次往外扩散,直到遍历完所有顶点。

dijkstra算法_最短路径问题——迪杰斯特拉算法(Dijkstra)相关推荐

  1. 数据结构——最小生成树之prime算法(与最短路径之迪杰斯特拉算法很像)

    最小生成树之prime算法 ***最小生成树:一个连通图的生成树中,所有边的权值加起来最小的生成树:称为最小生成树: [简介]:Prime算法可在加权连通图里搜索最小生成树.即:所有边的权值之和为最小 ...

  2. 最短路径之迪杰斯特拉(Dijkstra 算法)弗洛伊德算法(C语言完整代码实现)

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  3. 图的应用——最短路径(迪杰斯特拉算法)

    Content 1. 什么是最短路径 2. 迪杰斯特拉算法[O(n^2^)] 3. 代码 4. 迪杰斯特拉算法 VS 普利姆算法 1. 什么是最短路径 在一个带权有向图中,从某一顶点到另一顶点可能有很 ...

  4. java迪杰斯特拉算法实例,Java 图的最短路径dijstra(迪杰斯特拉)算法和拓扑排序

    一.图的最短路径从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径 图的最短路径有许多重要的应用. 例如:上图中v0-v8有9个点,可以看做不同的地点,现在要规 ...

  5. c++ 遍历所有点且距离最短_图解:最短路径之迪杰斯特拉算法

    小禹禹们,你们好,景禹最近已经开学,忙着准备毕业答辩的事情,这才抽身个大家更新文章,还请莫怪.生活实属不易,有时候让人有点儿焦头烂额,甚至想让景禹放弃继续更新文章,可是千百号人默默地关注者景禹,当然也 ...

  6. 最短路径(迪杰斯特拉算法)

    1 构造图 2 代码 package leaning.graph;/** 迪杰斯特拉算法求最短路径* * */ public class Dijkstra {// 表示V0顶点到v8顶点的最短路径的值 ...

  7. (王道408考研数据结构)第六章图-第四节4:最短路径之迪杰斯特拉算法(思想、代码、演示、答题规范)

    文章目录 一:BFS算法局限性 二:迪杰斯特拉(dijkstra)算法基本思想 三:迪杰斯特拉(dijkstra)算法代码实现 四:迪杰斯特拉(dijkstra)算法代码视频演示 五:迪杰斯特拉(di ...

  8. 最短路径之迪杰斯特拉算法

    基本思想: 设置顶点集合S并不断地作贪心选择来扩充这个集合.一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知. 初始时,S中仅含有源.设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称 ...

  9. 图的最短路径之迪杰斯特拉算法和弗洛伊德算法

    文章目录 一.迪杰斯特拉(Dijkstra)算法 1.定义描述 2.算法思想 3.算法步骤 4.算法图解 二.弗洛伊德(Floyd)算法 1.定义描述 2.算法思想 3.算法步骤 三.Dijkstra ...

最新文章

  1. java中Map遍历的四种方法
  2. jQuery插件之:对话框
  3. Linux下测试PHP和MySQL是否正确安装
  4. linux学习笔记:我的第一个shell脚本
  5. 用mysql语句备份_MySql常用备份及恢复语句
  6. 【计算机网络】物理层 : 奈氏准则 ( 失真 | “失真“ 影响因素 | 码间串扰 | 奈奎斯特定理 | 码元极限传输速率 | 信息极限传输速率 | 奈氏准则计算示例 )★
  7. 转录组-差异基因火山图
  8. 基于Flask+Echarts+爬虫的疫情监控系统
  9. 2021年美容师(初级)报名考试及美容师(初级)最新解析
  10. 4.22 使用裁切命令裁剪图片 [原创Ps教程]
  11. 百度地图开放平台天气预报查询API
  12. CSS3图片边框四个角剪切
  13. 固态硬盘是什么接口_固态硬盘那么多接口该怎么选?今天就和大家聊聊
  14. cropper.js oppoR9M 跑不起来相关问题==》已解决
  15. 不用网的cmd小游戏来啦
  16. 路由器上的usb接口有什么用_解决USB接口不够用,毕亚兹Type-C扩展坞体验测评
  17. 考研报名登录显示服务器错误,2018年考研报名常见问题:填写网报信息
  18. Twenty-sixth
  19. 会声会影2023最新版本剪辑视频的方法和步骤
  20. 【C语言】自定义类型(结构体、位段、枚举、联合体)与内存对齐

热门文章

  1. IDEA 不为人知的 5 个骚技巧!真香!
  2. 这样写Java,同事直呼666
  3. Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化
  4. MFC中CString类字符串用法小结
  5. 【Jetson-Nano】2.Tensorflow和Pytorch的安装
  6. android新建工程横屏,华为MatePad Pro构建的安卓平板横屏生态有何不同?来看看
  7. 精通ASP.NET MVC ——视图
  8. ASP.NET MVC 多语言开发简单案例
  9. html显示线条流动效果,css3实现线条流动效果
  10. maven安装过程以及手动添加jar包到本地仓库