Java中Dijkstra(迪杰斯特拉)算法


Posted in Java/Android onMay 20, 2022

简介

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。对应问题:在无向图G=(V,E)中,假设每条边E(i)的长度W(i),求由顶点V0到各节点的最短路径。

Java中Dijkstra(迪杰斯特拉)算法

工作过程

Dijkstra算法将顶点集合分为两组,一组记录已经求得最短路径的顶点记为finallyNodes,一组正在求解中的顶点记为processNodes,step1:finallyNodes中顶点最开始只有源节点,最短路径长度为0,而processNodes中包含除源节点以外的节点,并初始化路径长度,与源节点直接相连的记路径长度为权重,不相连的记为∞。step2:从process中选择路径长度最小的顶点,加入finallyNodes,并且更新processNodes,将与当前顶点相连的顶点路径长度更新为min(当前权重,当前顶点最短路径长度+当前顶点与顶点相连边权重)。step3:重复step2,直至processNodes数组为空。

Java中Dijkstra(迪杰斯特拉)算法

总体思路

这次我想先描述一下自己的大概思路,下面再写具体实现。首先为了方便,我采用的是邻接表存储图结构,邻接表是一个二维数组,值存储权重。根据上面工作过程中描述的内容,我们会有两个中间集合记录,finallyNodes记录的是最终结果,我们只需要将计算的结果往里面塞即可。但是processNodes却是一个不断变化更新的集合,其中的操作包括删除节点,更改节点值,查找节点值,同时我们每次需要拿出processNodes中记录的距离最小的值,所以ProcessNodes准备用最小堆来做,那再删除节点,更改节点值之后都需要调整堆为最小堆,java自带的优先队列没有提供更改节点值的操作,因此我们这里需要自己实现一个小根堆,支持以上操作。然后就中规中矩实现dijkstra算法即可。

实现

小根堆

如果对堆不太熟悉的可以先看看这篇文章:堆(优先队列),这里就不过多解释了,直接贴代码。这里堆中存的数据格式为int二维数组,存储节点下标位置和对应距离,排序按存储的距离进行排序。

public class MinHeap {
       List<int[][]> heap ;
       /**
        * 获取并移除堆顶元素,并调整堆
        * @return
        */
       public int[][] pop() {
           int[][] top = heap.get(0);
           heap.set(0, heap.get(heap.size() - 1));
           heap.remove(heap.size() - 1);
           //调整堆
           this.adjust(0, heap.size() - 1);
           return top;
      }

       /**
        * 判断是否为空
        * @return true/false
        */
       public boolean isEmpty() {
           if (null == this.heap) {
               return true;
          }
           if (this.heap.size() == 0) {
               return true;
          }
           return false;
      }
       /**
        * 修改index位置节点的value值,并调整最小堆(Java priorityQueue未提供)
        * @param index 修改节点位置
        * @param value 修改值
        */
       public void changeValue(int index, int value) {
           int src = heap.get(index)[0][1];
           heap.get(index)[0][1] = value;
           //直接比较当前值是变大还是变小,然后考虑是向上调整还是向下调整
           //则当前值可能往上移动
           if (src > value) {
               this.upAdjust(index);
               return;
          }
           this.adjust(index, heap.size() - 1);
      }

       public void upAdjust(int index) {
           //依次与双亲节点进行比较,小于双亲节点就直接交换。一直到根节点
           while (index > 0) {
               int parent = index >> 1;
               //双亲节点本来小于当前节点不需要进行调整
               if (heap.get(parent)[0][1] <= heap.get(index)[0][1]) {
                   break;
              }
               swap(index, parent);
               index = parent;
          }
      }
       
       /**
        * 初始化一个最小堆
        * @param nums
        */
       public void init(int[][] nums) {
           heap = new ArrayList<>(nums.length);
           for (int i = 0 ; i < nums.length; i ++) {
               int[][] temp = new int[1][2];
               temp[0][0] = nums[i][0];
               temp[0][1] = nums[i][1];
               heap.add(temp);
          }
           //从最后一个双亲节点开始将堆进行调整
           for (int i = nums.length / 2 ; i >= 0 ; -- i) {
               this.adjust(i, nums.length - 1);
          }
      }
       /**
        * 从当前index开始调节为最小堆
        * @param index 当前节点下标
        * @param end 最后一个节点下标
        */
       private void adjust(int index, int end) {
           //找到当前节点的孩子节点,将较小的节点与当前节点交换,一直往下,直至end
           while (index <= end) {
               //左孩子节点
               int left = index << 1;
               if (left + 1 <= end && heap.get(left + 1)[0][1] < heap.get(left)[0][1] ) {
                   //找到当前较小的节点
                   ++ left;
              }
               //没有孩子节点,或者当前的孩子节点均已大于当前节点,已符合最小堆,不需要进行调整
               if (left > end || heap.get(index)[0][1] <= heap.get(left)[0][1]) {
                   break;
              }
               swap(index, left);
               index = left;
          }
      }
       private void swap(int i, int j) {
           int[][] temp = heap.get(i);
           heap.set(i, heap.get(j));
           heap.set(j, temp);
      }
  }

Dijsktra

数据结构

图节点仅存储节点值,一个Node数组nodes,存储图中所有节点,一个二维数组adjacencyMatrix,存储图中节点之间边的权重,行和列下标与nodes数组下标对应。

//节点
Node[] nodes;

//邻接矩阵
int[][] adjacencyMatrix;
public class Node {
       private char value;
       Node(char value) {
           this.value = value;
      }
  }

初始化

初始化图values标志的图中所有节点值,edges标志图中边,数据格式为(node1的下标,node2的下标,边权重)

private void initGraph(char[] values, String[] edges) {
       nodes = new Node[values.length];
//初始化node节点
       for (int i = 0 ; i < values.length ; i ++) {
           nodes[i] = new Node(values[i]);
      }
       adjacencyMatrix = new int[values.length][values.length];
//初始化邻接表,同一个节点权重记为0,不相邻节点权重记为Integer.MAX_VALUE
       for (int i = 0 ; i < values.length ; i++) {
           for (int j = 0 ; j < values.length ; j ++) {
               if (i == j) {
                   adjacencyMatrix[i][j] = 0;
                   continue;
              }
               adjacencyMatrix[i][j] = Integer.MAX_VALUE;
               adjacencyMatrix[j][i] = Integer.MAX_VALUE;
          }
      }
//根据edges更新相邻节点权重值
       for (String edge : edges) {
           String[] node = edge.split(",");
           int i = Integer.valueOf(node[0]);
           int j = Integer.valueOf(node[1]);
           int weight = Integer.valueOf(node[2]);
           adjacencyMatrix[i][j] = weight;
           adjacencyMatrix[j][i] = weight;
      }
       visited = new boolean[nodes.length];

  }

初始化dijsktra算法必要的finallyNodes和processNodes

/**
* 标志对应下标节点是否已经处理,避免二次处理
*/
boolean[] visited;
   /**
    * 记录已经求得的最短路径 finallyNodes[0][0]记录node下标,finallyNodes[0][1]记录最短路径长度
    */
   List<int[][]> finallyNodes;
   /**
    * 记录求解过程目前的路径长度,因为每次取当前已知最短,所以最小堆进行记录
    * 但是java优先队列没有实现改变值,这里需要自己实现
    * 首先每次取出堆顶元素之后,堆顶元素加入finallyNodes,此时需要更新与当前元素相邻节点的路径长度
    * 然后重新调整小根堆
    * 首先:只会更新变小的数据,所以从变小元素开始往上进行调整,或者直接调用调整方法,从堆顶往下进行调整
    */
   MinHeap processNodes;
/**
    * 初始化,主要初始化finallyNodes和processNodes,finallyNodes加入源节点,processNodes加入其他节点
    * @param nodeIndex
    */
   private void initDijkstra(int nodeIndex) {
       finallyNodes = new ArrayList<>(nodes.length);
       processNodes = new MinHeap();
       int[][] node = new int[1][2];
       node[0][0] = nodeIndex;
       node[0][1] = adjacencyMatrix[nodeIndex][nodeIndex];
       finallyNodes.add(node);
       visited[nodeIndex] = true;
       int[][] process = new int[nodes.length - 1][2];
       int j = 0;
       for (int i = 0 ; i < nodes.length ; i++) {
           if (i == nodeIndex) {
               continue;
          }
           process[j][0] = i;
           process[j][1] = adjacencyMatrix[nodeIndex][i];
           ++ j;
      }
       //初始化最小堆
       processNodes.init(process);
  }

dijsktra算法实现

public void dijkstra() {
       //1。堆顶取出最小元素,加入finallyNodes
//2。将与堆顶元素相连节点距离更新,
       while (!processNodes.isEmpty()) {
           int[][] head = processNodes.pop();
           finallyNodes.add(head);
           int nodeIndex = head[0][0];
           visited[nodeIndex] = true;
           //跟堆顶元素相邻的元素
           for (int j = 0 ; j < nodes.length ; j ++) {
               //找到相邻节点
               if (visited[j] || Integer.MAX_VALUE == adjacencyMatrix[nodeIndex][j]) {
                   continue;
              }
               for (int i = 0 ; i < processNodes.heap.size() ; i++) {
                   int[][] node = processNodes.heap.get(i);
                   //找到节点并且值变小,需要调整
                   if (node[0][0] == j && node[0][1] > head[0][1] + adjacencyMatrix[nodeIndex][j]) {
                       processNodes.changeValue(i, head[0][1] + adjacencyMatrix[nodeIndex][j]);
                       break;
                  }
              }
          }

      }
  }

测试

public static void main(String[] args) {
       char[] values = new char[]{'A','B','C','D','E','F','G','H'};
       String[] edges = new String[]{"0,1,2","0,2,3","0,3,4","1,4,6","2,4,3","3,4,1","4,5,1","4,6,4","5,7,2","6,7,2"};
       Dijkstra dijkstra = new Dijkstra();
       dijkstra.initGraph(values, edges);
       int startNodeIndex = 0;
       dijkstra.initDijkstra(startNodeIndex);
       dijkstra.dijkstra();
       for (int[][] node : dijkstra.finallyNodes) {
           System.out.println(dijkstra.nodes[node[0][0]].value + "距离" + dijkstra.nodes[startNodeIndex].value + "最短路径为:" + node[0][1]);
      }
  }

Java中Dijkstra(迪杰斯特拉)算法

以上就是详解Java中Dijkstra(迪杰斯特拉)算法的图解与实现的详细内容!


Tags in this post...

Java/Android 相关文章推荐
java Nio使用NioSocket客户端与服务端交互实现方式
Jun 15 Java/Android
浅谈什么是SpringBoot异常处理自动配置的原理
Jun 21 Java/Android
详解Java线程池是如何重复利用空闲线程的
Jun 26 Java/Android
SpringBoot集成Druid连接池连接MySQL8.0.11
Jul 02 Java/Android
Java使用jmeter进行压力测试
Jul 09 Java/Android
springboot中rabbitmq实现消息可靠性机制详解
Sep 25 Java/Android
Eclipse+Java+Swing+Mysql实现电影购票系统(详细代码)
Jan 18 Java/Android
Flutter集成高德地图并添加自定义Maker的实践
Apr 07 Java/Android
JAVA springCloud项目搭建流程
May 11 Java/Android
Spring 使用注解开发
May 20 Java/Android
利用正则表达式匹配浮点型数据
May 30 Java/Android
volatile保证可见性及重排序方法
Aug 05 Java/Android
Android Studio实现带三角函数对数运算功能的高级计算器
May 20 #Java/Android
springcloud整合seata
springboot读取nacos配置文件
May 20 #Java/Android
Android studio 简单计算器的编写
May 20 #Java/Android
mybatis 获取更新记录的id
May 20 #Java/Android
Android Studio 计算器开发
May 20 #Java/Android
Android Studio实现简易进制转换计算器
May 20 #Java/Android
You might like
destoon安全设置中需要设置可写权限的目录及文件
2014/06/21 PHP
PHP之sprintf函数用法详解
2014/11/12 PHP
ThinkPHP中redirect用法分析
2014/12/05 PHP
PHP数组array类常见操作示例
2020/05/15 PHP
显示今天的日期js代码(阳历和农历)
2014/09/30 Javascript
js实现表单检测及表单提示的方法
2015/08/14 Javascript
基于JQuery打造无缝滚动新闻步骤详解
2016/03/31 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
Angular.js中用ng-repeat-start实现自定义显示
2016/10/18 Javascript
Javascript别踩白块儿(钢琴块儿)小游戏实现代码
2017/07/20 Javascript
vue中将html字符串转换成html后遇到的问题小结
2018/12/10 Javascript
微信小程序实现禁止分享代码实例
2019/10/19 Javascript
CKEditor扩展插件:自动排版功能autoformat插件实现方法详解
2020/02/06 Javascript
vue实现桌面向网页拖动文件的示例代码(可显示图片/音频/视频)
2021/03/01 Vue.js
[55:23]VGJ.T vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
深入理解Python 代码优化详解
2014/10/27 Python
python多线程操作实例
2014/11/21 Python
Python base64编码解码实例
2015/06/21 Python
python爬虫实战之最简单的网页爬虫教程
2017/08/13 Python
python实现下载pop3邮件保存到本地
2018/06/19 Python
Python使用cx_Freeze库生成msi格式安装文件的方法
2018/07/10 Python
Django中的FBV和CBV用法详解
2019/09/15 Python
Python描述数据结构学习之哈夫曼树篇
2020/09/07 Python
10个很棒的 CSS3 开发工具 推荐
2011/05/16 HTML / CSS
几个解决兼容IE6\7\8不支持html5标签的几个方法
2013/01/07 HTML / CSS
世界最大域名注册商:GoDaddy
2016/07/24 全球购物
澳大利亚儿童鞋在线:The Trybe
2019/07/16 全球购物
工程业务员岗位职责
2013/12/31 职场文书
企业管理毕业生求职信
2014/03/11 职场文书
内勤主管岗位职责
2014/04/03 职场文书
求职自荐信的格式
2014/04/07 职场文书
演讲稿祖国在我心中
2014/05/04 职场文书
预备党员期盼十八届四中全会召开思想汇报
2014/10/17 职场文书
营业用房租赁协议书
2014/11/26 职场文书
2015年酒店服务员工作总结
2015/05/18 职场文书
SQL语法CONSTRAINT约束操作详情
2022/01/18 MySQL