最短路径问题是图论研究中的一个经典的算法问题,旨在寻找图中两个节点之间的最短路径,最常用的算法有Dijkstra算法、SPFA算法\Bellman-Ford算法、Floyd算法\Floyd-Warshall算法、Johnson算法等,这篇博客将重点介绍Dijkstra算法。

迪杰斯特拉算法

迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。具体的计算规则我们可以通过下图进行查看。

通过这幅图(如果图片无法正确显示,请通过百度百科查看)我们可以简单的理解迪杰斯特拉算法算法的基础思路,下面我们就通过Java来实现这个算法。

先给出一个无向图

用Dijkstra算法找出以A为起点的单源最短路径步骤如下

算法实现

在具体的实现之前,我们先有一个基础的约定,就是途中的每一个节点我们都用正整数进行编码,相邻两点之间的距离是正整数,图中两个直接相邻两点的距离我们保存到map中,也就是求最短距离我们需要实现这样的一个方法:

public MinStep getMinStep(int start, int end, final HashMap> stepLength);

第一个参数是起始节点的编号,第二个参数是终点节点的编号,第三个参数是图中直接相邻两个节点的距离组成的map。关于第三个参数,会在后面做详细的介绍。这里我们定义一个接口,用于计算两点之间的最短路径,具体如下:

/**

*@Description:

*/

package com.lulei.distance;

import java.util.HashMap;

import com.lulei.distance.bean.MinStep;

public interface Distance {

public static final MinStep UNREACHABLE = new MinStep(false, -1);

/**

* @param start

* @param stepLength

* @Author:lulei

* @Description: 起点到终点的最短路径

*/

public MinStep getMinStep(int start, int end, final HashMap> stepLength);

}

一、方法返回值介绍

上面方法的返回值是我们自定义的一个数据类型,下面我们通过代码来看其具体的数据结构:

/**

*@Description:

*/

package com.lulei.distance.bean;

import java.util.List;

public class MinStep {

private boolean reachable;//是否可达

private int minStep;//最短步长

private List step;//最短路径

public MinStep() {

}

public MinStep(boolean reachable, int minStep) {

this.reachable = reachable;

this.minStep = minStep;

}

public boolean isReachable() {

return reachable;

}

public void setReachable(boolean reachable) {

this.reachable = reachable;

}

public int getMinStep() {

return minStep;

}

public void setMinStep(int minStep) {

this.minStep = minStep;

}

public List getStep() {

return step;

}

public void setStep(List step) {

this.step = step;

}

}

其中最短路径的那个List数组保存了从起点到终点最短路径所经历的节点。

二、每一个节点的最优前一节点

在迪杰斯特拉算法中我们需要保存从起点开始到每一个节点最短步长,这也是图中需要比较得出的步长,同时我们还需要存储该步长下的前一个节点是哪个,这样我们就可以通过终点一个一个往前推到起点,这样就出来了完整的最优路径。

/**

*@Description:

*/

package com.lulei.distance.bean;

public class PreNode {

private int preNodeNum;// 最优 前一个节点

private int nodeStep;// 最小步长

public PreNode(int preNodeNum, int nodeStep) {

this.preNodeNum = preNodeNum;

this.nodeStep = nodeStep;

}

public int getPreNodeNum() {

return preNodeNum;

}

public void setPreNodeNum(int preNodeNum) {

this.preNodeNum = preNodeNum;

}

public int getNodeStep() {

return nodeStep;

}

public void setNodeStep(int nodeStep) {

this.nodeStep = nodeStep;

}

}

三、迪杰斯特拉算法计算过程中需要关注的变量

从介绍迪杰斯特拉算法的图中,我们知道在计算的过程中我们需要保存起点到各个节点的最短距离、已经计算过的节点、下次需要计算节点队列和图中相邻两个节点的距离。我们通过代码来看下具体的定义:

//key1节点编号,key2节点编号,value为key1到key2的步长

private HashMap> stepLength;

//非独立节点个数

private int nodeNum;

//移除节点

private HashSet outNode;

//起点到各点的步长,key为目的节点,value为到目的节点的步长

private HashMap nodeStep;

//下一次计算的节点

private LinkedList nextNode;

//起点、终点

private int startNode;

private int endNode;

我们这里看下stepLength这个属性,它保存了图中相邻两个节点之间的距离,比如key1=1;key2=3;value=9;这代表的意义就是从节点1到节点3的距离是9。通过这样的存储,我们就需要把图中每两个相邻的点保存到这个类型的map中。

四、属性初始化

在开始计算之前,我们需要对这些属性进行初始化,具体如下:

private void initProperty(int start, int end) {

outNode = new HashSet();

nodeStep = new HashMap();

nextNode = new LinkedList();

nextNode.add(start);

startNode = start;

endNode = end;

}

这一步我们需要把起点添加到下一次需要计算的节点队列中。

五、迪杰斯特拉算法

这一步也就是迪杰斯特拉算法的核心部分,在计算的过程中,我们需要进行如下步骤:

1)判断是否达到终止条件,如果达到终止条件,结束本次算法,如果没有达到,执行下一步;(终止条件:下一次需要计算的节点队列没有数据或已经计算过的节点数等于节点总数)

2)获取下一次计算的节点A;

3)从起点到各节点之间的最短距离map中获取到达A点的最小距离L;

4)获取A节点的可达节点B,计算从起点先到A再到B是否优于已有的其他方式到B,如果优于,则更新B节点,否则不更新;

5)判断B是否是已经移除的节点,如果不是移除的节点,把B添加到下一次需要计算的节点队列中,否则不做操作;

6)判断A节点是否还有除B以外的其他节点,如果有,执行第4)步,否则执行下一步;

7)将A节点从下一次需要计算的节点中移除添加到已经计算过的节点中;

8)执行第一步。

我们来看下具体的代码实现:

private void step() {

if (nextNode == null || nextNode.size()

return;

}

if (outNode.size() == nodeNum) {

return;

}

//获取下一个计算节点

int start = nextNode.removeFirst();

//到达该节点的最小距离

int step = 0;

if (nodeStep.containsKey(start)) {

step = nodeStep.get(start).getNodeStep();

}

//获取该节点可达节点

HashMap nextStep = stepLength.get(start);

Iterator> iter = nextStep.entrySet().iterator();

while (iter.hasNext()) {

Entry entry = iter.next();

Integer key = entry.getKey();

//如果是起点到起点,不计算之间的步长

if (key == startNode) {

continue;

}

//起点到可达节点的距离

Integer value = entry.getValue() + step;

if ((!nextNode.contains(key)) && (!outNode.contains(key))) {

nextNode.add(key);

}

if (nodeStep.containsKey(key)) {

if (value

nodeStep.put(key, new PreNode(start, value));

}

} else {

nodeStep.put(key, new PreNode(start, value));

}

}

//将该节点移除

outNode.add(start);

//计算下一个节点

step();

}

六、组装最短路径返回结果

通过前面的计算,已经计算出了起点到各个节点的最短路径,下面就需要组装起点到终点的最短路径,在计算最短距离下的路径方式,我们需要从终点依次往前推,即到达终点最短距离下的前一个节点是A,到达A节点最短距离下的前一节点是B,直到找到起点终止。

private MinStep changeToMinStep() {

MinStep minStep = new MinStep();

minStep.setMinStep(nodeStep.get(endNode).getNodeStep());

minStep.setReachable(true);

LinkedList step = new LinkedList();

minStep.setStep(step);

int nodeNum = endNode;

step.addFirst(nodeNum);

while (nodeStep.containsKey(nodeNum)) {

int node = nodeStep.get(nodeNum).getPreNodeNum();

step.addFirst(node);

nodeNum = node;

}

return minStep;

}

七、接口定义方法实现

public MinStep getMinStep(int start, int end, final HashMap> stepLength) {

this.stepLength = stepLength;

this.nodeNum = this.stepLength != null ? this.stepLength.size() : 0;

//起点、终点不在目标节点内,返回不可达

if (this.stepLength == null || (!this.stepLength.containsKey(start)) || (!this.stepLength.containsKey(end))) {

return UNREACHABLE;

}

initProperty(start, end);

step();

if (nodeStep.containsKey(end)) {

return changeToMinStep();

}

return UNREACHABLE;

}

八、迪杰斯特拉算法完整代码

/**

*@Description:

*/

package com.lulei.distance.dijkstra;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Iterator;

import java.util.LinkedList;

import java.util.Map.Entry;

import com.lulei.distance.Distance;

import com.lulei.distance.bean.MinStep;

import com.lulei.distance.bean.PreNode;

public class DistanceDijkstraImpl implements Distance{

//key1节点编号,key2节点编号,value为key1到key2的步长

private HashMap> stepLength;

//非独立节点个数

private int nodeNum;

//移除节点

private HashSet outNode;

//起点到各点的步长,key为目的节点,value为到目的节点的步长

private HashMap nodeStep;

//下一次计算的节点

private LinkedList nextNode;

//起点、终点

private int startNode;

private int endNode;

/**

* @param start

* @param end

* @param stepLength

* @return

* @Author:lulei

* @Description: start 到 end 的最短距离

*/

public MinStep getMinStep(int start, int end, final HashMap> stepLength) {

this.stepLength = stepLength;

this.nodeNum = this.stepLength != null ? this.stepLength.size() : 0;

//起点、终点不在目标节点内,返回不可达

if (this.stepLength == null || (!this.stepLength.containsKey(start)) || (!this.stepLength.containsKey(end))) {

return UNREACHABLE;

}

initProperty(start, end);

step();

if (nodeStep.containsKey(end)) {

return changeToMinStep();

}

return UNREACHABLE;

}

/**

* 返回最短距离以及路径

*/

private MinStep changeToMinStep() {

MinStep minStep = new MinStep();

minStep.setMinStep(nodeStep.get(endNode).getNodeStep());

minStep.setReachable(true);

LinkedList step = new LinkedList();

minStep.setStep(step);

int nodeNum = endNode;

step.addFirst(nodeNum);

while (nodeStep.containsKey(nodeNum)) {

int node = nodeStep.get(nodeNum).getPreNodeNum();

step.addFirst(node);

nodeNum = node;

}

return minStep;

}

/**

* @param start

* @Author:lulei

* @Description: 初始化属性

*/

private void initProperty(int start, int end) {

outNode = new HashSet();

nodeStep = new HashMap();

nextNode = new LinkedList();

nextNode.add(start);

startNode = start;

endNode = end;

}

/**

* @param end

* @Author:lulei

* @Description:

*/

private void step() {

if (nextNode == null || nextNode.size()

return;

}

if (outNode.size() == nodeNum) {

return;

}

//获取下一个计算节点

int start = nextNode.removeFirst();

//到达该节点的最小距离

int step = 0;

if (nodeStep.containsKey(start)) {

step = nodeStep.get(start).getNodeStep();

}

//获取该节点可达节点

HashMap nextStep = stepLength.get(start);

Iterator> iter = nextStep.entrySet().iterator();

while (iter.hasNext()) {

Entry entry = iter.next();

Integer key = entry.getKey();

//如果是起点到起点,不计算之间的步长

if (key == startNode) {

continue;

}

//起点到可达节点的距离

Integer value = entry.getValue() + step;

if ((!nextNode.contains(key)) && (!outNode.contains(key))) {

nextNode.add(key);

}

if (nodeStep.containsKey(key)) {

if (value

nodeStep.put(key, new PreNode(start, value));

}

} else {

nodeStep.put(key, new PreNode(start, value));

}

}

//将该节点移除

outNode.add(start);

//计算下一个节点

step();

}

}

代码测试

对于上述代码的测试,我们还是使用我们事例图形中的例子,计算从节点1到节点5的最短距离。

/**

*@Description:

*/

package com.lulei.distance.test;

import java.util.HashMap;

import com.lulei.distance.Distance;

import com.lulei.distance.bean.MinStep;

import com.lulei.distance.dijkstra.DistanceDijkstraImpl;

import com.lulei.util.JsonUtil;

public class DistanceTest {

public static void main(String[] args) {

// TODO Auto-generated method stub

HashMap> stepLength = new HashMap>();

HashMap step1 = new HashMap();

stepLength.put(1, step1);

step1.put(6, 14);

step1.put(3, 9);

step1.put(2, 7);

HashMap step2 = new HashMap();

stepLength.put(2, step2);

step2.put(1, 7);

step2.put(3, 10);

step2.put(4, 15);

HashMap step3 = new HashMap();

stepLength.put(3, step3);

step3.put(1, 9);

step3.put(2, 10);

step3.put(4, 11);

step3.put(6, 2);

HashMap step4 = new HashMap();

stepLength.put(4, step4);

step4.put(2, 15);

step4.put(5, 5);

step4.put(3, 11);

HashMap step5 = new HashMap();

stepLength.put(5, step5);

step5.put(6, 9);

step5.put(4, 5);

HashMap step6 = new HashMap();

stepLength.put(6, step6);

step6.put(1, 14);

step6.put(5, 9);

step6.put(3, 2);

Distance distance = new DistanceDijkstraImpl();

MinStep step = distance.getMinStep(1, 5, stepLength);

System.out.println(JsonUtil.parseJson(step));

}

}

这里组装相邻两个节点之间的距离用了大量的代码,我们看下输出结果:

{"reachable":true,"minStep":20,"step":[1,3,6,5]}

最后的思考

最短路径算法在现实生活中其实有很多的用处,比如迷宫解法、路径规划、路由寻址等等,这些问题看似很复杂,其实只需要做对应的转化后还是可以用最基础的算法解决的。

java 迪杰斯特拉_JAVA实现最短距离算法之迪杰斯特拉算法相关推荐

  1. java所有的质因数_Java实现分解任意输入数的质因数算法示例

    本文实例讲述了java实现分解任意输入数的质因数算法.分享给大家供大家参考,具体如下: 分解任意输入数的质因数: 质因数概念:任何一个合数都可以写成几个质数相乘的形式.其中每个质数都是这个合数的因数, ...

  2. java质因数的分解_Java实现分解任意输入数的质因数算法示例

    本文实例讲述了Java实现分解任意输入数的质因数算法.分享给大家供大家参考,具体如下: 分解任意输入数的质因数: 质因数概念:任何一个合数都可以写成几个质数相乘的形式.其中每个质数都是这个合数的因数, ...

  3. java循环左一_java实现循环左移和右移的简单算法

    java实现循环左移和右移的简单算法 byte a=112,用程序实现,将其循环左移三位和右移三位. 112的二进制原码:0111 0000 112循环左移3位后的二进制码:1000 0011 112 ...

  4. java迪杰斯特拉算法介绍_178-迪杰斯特拉(Dijkstra)算法基本介绍

    2.网上数据结构和算法的课程不少,但存在两个问题: 1)授课方式单一,大多是照着代码念一遍,数据结构和算法本身就比较难理解,对基础好的学员来说,还好一点,对基础不好的学生来说,基本上就是听天书了 2) ...

  5. 算法学习--迪杰斯特拉和弗洛伊德

    一.迪杰斯特拉 求解图中由起点开始到其他节点的最短路径问题(权值不能为负). 主要思想:在有权值的图中,直接和起点相连的节点他们的最短路径为和起点相连的边的权值,和起点没有直接相连的节点默认为该点和起 ...

  6. [转]最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现

    最短路径算法-Dijkstra(迪杰斯特拉)算法分析与实现(C/C++) Dijkstra算法 ----------- 最后更新时间:2011.9.25 ----------- Dijkstra(迪杰 ...

  7. 0096 克鲁斯卡尔算法,迪杰斯特拉算法

    /*  * 克鲁斯卡尔算法  * 1.用来求加权连通图的最小生成树的算法  * 2.思想:按照权值从小到大的顺序,选择n-1条边,并保证这n-1条边不构成回路  * 3.先构造一个只含n个顶点的森林, ...

  8. 图 相关算法~从头学算法【广搜、 深搜、 拓扑排序、 并查集、 弗洛伊德算法、迪杰斯特拉算法】

    图的相关主流算法主要有: 广度优先搜索 深度优先搜索 拓扑排序 并查集 多源最短路径(弗洛伊德算法) 单源最短路径(迪杰斯特拉算法) 其中呢,最基本的是前两种,也就是平时常用的广搜和深搜,本文中将概要 ...

  9. 图论基础知识--最小生成树算法kruskal(克鲁斯克尔)和普里姆算法(Prim算法);最短路径算法Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)

    一.基础知识   有向图   无向图 以无向图为例: 邻接矩阵: 度矩阵(对角矩阵): 二.最小生成树 应用:将网络顶点看着城市,边看着城市之间通讯网,边的权重看着成本,根据最小生成树可以构建城市之间 ...

  10. 数据结构第十二天——普利姆算法和迪杰斯特拉算法

    普利姆(Prim)算法求最小生成树,也就是在包含 n个顶点的连通图中,找出只有(n-1)条边包含所有 n个顶点的连通子图,也就是所谓的极小连通子图 最小生成树:给定一个带权的无向连通图,如何选取一棵生 ...

最新文章

  1. docker java 最小镜像_docker构建JDK最小镜像
  2. sparkcore写mysql_spark读写mysql
  3. VTK:vtkDelaunay2D用法实战
  4. C/C++人机猜拳游戏
  5. 云开发技术应用python_云开发技术应用(Python)
  6. js 导出pdf上传至oss_js实现oss文件上传及一些问题
  7. Http长连接的例子_亲测可用哦
  8. java显示文件_java 显示文件夹结构
  9. 9.20 模拟试题
  10. xcode里面找不到头文件
  11. 深入剖析WebRTC事件机制之Sigslot
  12. 玩游戏学python的网站_娱教于乐!四大游戏类编程网站,学Python再也不枯燥无味了...
  13. Android Socket编程【转】http://duguyidao.iteye.com/blog/1069736
  14. xtrabackup 2.4.3 BUG
  15. bigint hive java类型_【干货】Hive常用函数大全
  16. 苹果计算机快捷键设置,苹果电脑怎么改快捷键
  17. 家里可以装网吧无盘服务器吗,我在家里想装个网吧系统,求好的网吧系统。请专业的来个连接我...
  18. Hadoop经典案例——单词统计
  19. 【图像识别-指纹识别】指纹特征提取附matlab代码
  20. rtx2060什么水平_RTX2060值得买吗

热门文章

  1. linux51单片机烧录程序,单片机成长之路(51基础篇) - 006 在Linux下搭建51单片机的开发烧写环境...
  2. win7下安装Jira(破解加汉化)
  3. 9.12测试(二)——国际象棋
  4. html 5标签读音,radish读音
  5. 用MATLAB仿真AM调制
  6. linux虚拟机usb网卡驱动,【Vbox】centos虚拟机安装usb网卡驱动
  7. win10系统word2019显示目录只显示部分一级二级解决办法
  8. C++和数据结构考试总结
  9. lol12.11服务器维护,英雄联盟11.1版本更新维护公告 装备调整新赛季开始
  10. keil中断函数的写法_中断函数写法的比较