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

a42f533b5255b42dc2910a308031b2b2.png

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

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

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

ae75504303e2bbccaea0309fe294f452.png

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

思路

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

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

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

    索引123456
    最小距离06010null50null
  2. 从顶点 2 处中转,顶点 2 能到达顶点 4,距离为 35,所以顶点 1 到顶点 4 的距离为 60+35=95,数组状态如下:

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

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

代码实现

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

另外我们还需要定义顶点之间的边,一条边有起点和终点,还有边的权重信息,也就是长度,用类 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);
}

测试结果:

ad1f478c972dc07cffc89cc535ea776f.png

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ES6 深拷贝_你别自以为是:ES6误区 之 Object.assign()、const

文/北妈阅读本文需要 2.3分钟一很久没发技术文&#xff0c;今天北妈在新开一个技术系列&#xff1a;“别自以为是&#xff0c;1分钟走出JS常见误区“&#xff0c;里面我会每期挑选几个常见基础属性&#xff0c;讲一讲里面最容易被人忽略和认知错误的误区。帮助大家更好的掌握基…

opengl实现三维动画简单代码_使用Python简单实现马赛克拼图!内附完整代码

今天小编带大家使用python简单实现马赛克拼图&#xff0c;内容比以往会稍长一些&#xff0c;各位看官老爷可以慢慢细读&#xff0c;若有不足之处还望请斧正&#xff0c;闲话不多说&#xff0c;请看文章。先看原图&#xff1a;效果图&#xff1a;思路&#xff1a;拼图的原理其实…

耳机不支持android,安卓手机为什么不能用EarPods耳机 原因分析【图解】

相信很多人都有这样的疑问&#xff0c;iPhone和安卓手机的耳机孔是一样的&#xff0c; 安卓手机为什么不能用EarPods耳机? 这是什么原因&#xff1f;本次在这里就给大家分析下。安卓手机为什么不能用EarPods耳机?目前市面上大多数的手机仍采用3.5mm的耳机接口。但很多人不知道…

python自然语言处理库_Python 自然语言处理(NLP)工具库汇总

最近正在用nltk 对中文网络商品评论进行褒贬情感分类&#xff0c;计算评论的信息熵&#xff08;entropy&#xff09;、互信息&#xff08;point mutual information&#xff09;和困惑值&#xff08;perplexity&#xff09;等&#xff08;不过这些概念我其实也还理解不深...只是…

android代码清除锁屏密码,清除Android手机锁屏密码的三个小妙招

大家有没有把锁屏密码忘记过&#xff1f;发生这种情况的概率应该很小吧&#xff0c;但为了以防万一大家还是看一下本文的解锁教程吧&#xff01;这种解锁的方法要求&#xff0c;被锁上的手机是联网的状态&#xff0c;并登录了google账号&#xff0c;账号密码已知。如果以上条件…

c语言数组最大可定义多少位_C语言求数组的最大值三种方法

/* 黄哥Python培训 黄哥所写*/#include int maxValue(int* arr, int n);int maxRecursionValue(int* arr, int n);int maxDividAndConquerValue(int* arr, int left, int right);int main(void) { int arr[] {500, -1, 30, 7, 99, 12}; printf("数组中的元素最大…

android 百度map 一个layout加载多个mapview,android 百度地图API 使用Marker和InfoWindow

前言&#xff1a;在android开发过程中&#xff0c;百度地图的使用是比较普遍的&#xff0c;但是如何使用&#xff0c;使用什么版本的百度API还是需要一些讲究。在项目过程中&#xff0c;需要用到百度地图的marker和InfoWindow的功能。标注覆盖物(百度地图官方图)布局文件很简单…

python数据分析实验报告_Python 数据分析入门实战

本训练营中&#xff0c;我们将学习怎么样使用 Python 进行数据分析。课程将从数据分析基础开始&#xff0c;一步步深入讲解。从 Python 的基础用法到数据分析的各种算法&#xff0c;并结合各种实例&#xff0c;讲解数据分析过程中的方方面面。 课程内容将分为以下四个部分&…

go 写文件_如何在 Ubuntu 20.04 上安装 Go

本文最先发布在&#xff1a;如何在 Ubuntu 20.04 上安装 Go​www.itcoder.techGo&#xff0c;通常被称为 golang&#xff0c;它是一门由 Google 创建的现代化的开源编程语言&#xff0c;它允许你构建实时并且高效的应用。很多流行的应用程序&#xff0c;例如 Kubernetes&#x…

java汽车管理系统_坑爹!花费2亿耗时2年,网站没建完Java都写不好,顶级咨询公司埃森哲被告上法庭...

乾明 发自 凹非寺 量子位 报道 | 公众号 QbitA耗费2个多亿&#xff0c;耗时2年多&#xff0c;连一个可用的网站或者APP都没有交付出来。想要完工&#xff1f;那就再交1000万美元。这件事的受害方、美国汽车租赁公司赫兹(Hertz)一怒之下&#xff0c; 将顶级咨询公司埃森哲(Accen…

Android接口一般定义格式,Android开发规范

原标题&#xff1a;Android开发规范一.书写规范1. 编码方式统一用UTF-8.2. 花括号不要单独一行&#xff0c;和它前面的代码同一行。而且&#xff0c;花括号与前面的代码之间用一个空格隔开。3. 空格的使用if、else、for、switch、while等逻辑关键字与后面的语句留一个空格隔开。…

c++将小写转换为大写函数_必须掌握的基础函数组合应用技巧,提高效率,准时下班...

点击上方"Excel函数公式"免费订阅货币&#xff0c;生活中必不可少的东西&#xff0c;是物品价值等的直接体现&#xff0c;在实际的工作中也经常遇到&#xff0c;如果给定的数据中&#xff0c;要对其进行格式的设置&#xff0c;你会怎么做&#xff1f;一、Dollar函数&…

jenkins使用哪个版本号_Linux下安装JDK及jenkins

往期相关文章推荐&#xff1a;Linux ping不通域名安装JDK依赖(8/11)一.(推荐)// 查看yum仓库中可安装的jdk版本yum -y list java*// 安装示例yum install -y java-1.8.0-openjdk-devel.x86_64java --version 二.1.获取JDK安装包&#xff0c;可以win下下载&#xff0c;再用scp…

骁龙660鸿蒙系统,骁龙660双摄测试机偷跑 核心数/GPU证实

中关村在线消息&#xff1a;高通会在今年推出一款全新的中端处理器——骁龙660。此前有网友在微博上曝光一组疑似骁龙660的跑分&#xff0c;安兔兔总成绩为105576分。现在&#xff0c;微博上又出现搭载骁龙660双摄工程机的谍照&#xff0c;该机支持2K分辨率&#xff0c;采用6GB…

tensorflow 模型可视化_基于tensorflow-2.x的yolov3实现

YOLO v3可以说是单阶段检测器中的佼佼者&#xff0c;融合了多个框架的优势&#xff0c;在保持模型简洁性的同时&#xff0c;性能上也在当时达到了stoa。YOLO v3的主干网络是darknet-53的前面的52层&#xff0c;所以它是一个全卷积网络&#xff0c;并且为了降低池化带来的梯度负…

android闹钟延时,android闹钟定时启动延时或者直接不启动

自己写的android闹钟功能&#xff0c;需要实现timepicker选择完成后将选择的时间设定为闹钟的启动时间&#xff0c;但是不管怎么改总是没法定时启动alertDialog new AlertDialog.Builder(context).setView(view).setCustomTitle(viewTitle).setNegativeButton("确定"…

switch语句可以被代替吗_爬楼梯可以代替跑步吗?

转载&#xff1a;有很多人在下雨天选择爬楼梯作为运动方式&#xff0c;前几天就有人问老王&#xff1a;爬楼梯可以代替跑步吗&#xff1f;爬楼梯是在一个坡度上下移动&#xff0c;上楼梯时&#xff0c;腿部需要承受自身体重1.5-2.5倍的重量&#xff1b;下楼梯时则要承受自身体重…

gsonformat插件_吐血推荐珍藏的IDEA插件

之前给大家推荐了一些我自己常用的VS Code插件&#xff0c;很多同学表示很受用&#xff0c;并私信我说要再推荐一些IDEA插件。作为一名职业Java程序员/业余js开发者&#xff0c;我平时还是用IDEA比较多&#xff0c;所以也确实珍藏了一些IDEA插件。今天就一并分享给大家。在最开…

html城市手机搜索,原生js实现html手机端城市列表索引选择城市

本文实例为大家分享了js实现手机端城市列表索引选择城市的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下html部分&#xff1a;定位城市上海市css部分&#xff1a;*{margin: 0;padding: 0;list-style: none;}html{font-size: 12px;}body {background-color: #f5f5f5;…

html 图片使用scale,缩放:scale() - CSS3 | 绿叶学习网

在CSS3中&#xff0c;我们可以使用transform属性的scale()方法来实现元素的缩放效果。缩放&#xff0c;指的是“缩小”和“放大”的意思。语法&#xff1a;transform: scaleX(x); /*沿X轴方向缩放*/transform: scaleY(y); /*沿Y轴方向缩放*/transform: scale(x, y); /*沿X轴和Y…