java实现dijkstra最短路径寻路算法

【引用】迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。

它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

基本思想

通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。

此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。

初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。

操作步骤

(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。

得益于csdn另外一篇博客的算法,我对此做了一些改进。

构建地图:

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 地图
 * @author jake
 * @date 2014-7-26-下午10:40:10
 * @param <T> 节点主键
 */
public class Maps<T> {

 /**
 * 所有的节点集合
 * 节点Id - 节点
 */
 private Map<T, Node<T>> nodes = new HashMap<T, Node<T>>();

 /**
 * 地图构建器
 *
 * @author jake
 * @date 2014-7-26-下午9:47:44
 */
 public static class MapBuilder<T> {

 /**
  * map实例
  */
 private Maps<T> map = new Maps<T>();

 /**
  * 构造MapBuilder
  *
  * @return MapBuilder
  */
 public MapBuilder<T> create() {
  return new MapBuilder<T>();
 }

 /**
  * 添加节点
  *
  * @param node 节点
  * @return
  */
 public MapBuilder<T> addNode(Node<T> node) {
  map.nodes.put(node.getId(), node);
  return this;
 }

 /**
  * 添加路线
  *
  * @param node1Id 节点Id
  * @param node2Id 节点Id
  * @param weight 权重
  * @return
  */
 public MapBuilder<T> addPath(T node1Id, T node2Id, int weight) {
  Node<T> node1 = map.nodes.get(node1Id);
  if (node1 == null) {
  throw new RuntimeException("无法找到节点:" + node1Id);
  }

  Node<T> node2 = map.nodes.get(node2Id);
  if (node2 == null) {
  throw new RuntimeException("无法找到节点:" + node2Id);
  }

  node1.getChilds().put(node2, weight);
  node2.getChilds().put(node1, weight);
  return this;
 }

 /**
  * 构建map
  * @return map
  */
 public Maps<T> build() {
  return this.map;
 }

 }

 /**
 * 节点
 *
 * @author jake
 * @date 2014-7-26-下午9:51:31
 * @param <T> 节点主键类型
 */
 public static class Node<T> {

 /**
  * 节点主键
  */
 private T id;

 /**
  * 节点联通路径
  * 相连节点 - 权重
  */
 private Map<Node<T>, Integer> childs = new HashMap<Node<T>, Integer>();

 /**
  * 构造方法
  * @param id 节点主键
  */
 public Node(T id) {
  this.id = id;
 }

 /**
  * 获取实例
  * @param id 主键
  * @return
  */
 public static <T> Node<T> valueOf(T id) {
  return new Node<T>(id);
 }

 /**
  * 是否有效
  * 用于动态变化节点的可用性
  * @return
  */
 public boolean validate() {
  return true;
 }

 public T getId() {
  return id;
 }

 public void setId(T id) {
  this.id = id;
 }

 public Map<Node<T>, Integer> getChilds() {
  return childs;
 }

 protected void setChilds(Map<Node<T>, Integer> childs) {
  this.childs = childs;
 }

 @Override
 public String toString() {
  StringBuilder sb = new StringBuilder();
  sb.append(this.id).append("[");
  for (Iterator<Entry<Node<T>, Integer>> it = childs.entrySet().iterator(); it.hasNext();) {
  Entry<Node<T>, Integer> next = it.next();
  sb.append(next.getKey().getId()).append("=").append(next.getValue());
  if (it.hasNext()) {
   sb.append(",");
  }
  }
  sb.append("]");
  return sb.toString();
 }

 }

 /**
 * 获取地图的无向图节点
 * @return 节点Id - 节点
 */
 public Map<T, Node<T>> getNodes() {
 return nodes;
 }

}

开始寻路:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.my9yu.sanguohun2.utils.dijkstra.Maps.MapBuilder;

/**
 * 迪杰斯特拉(Dijkstra)图最短路径搜索算法
 * <br/>每次开始新的搜索需要创建此类对象
 * @param <T> 节点的主键类型
 * @author jake
 * @date 2014-7-26-下午9:45:07
 */
public class MapSearcher<T> {

 /**
 * 最短路径搜索结果类
 * @author jake
 * @date 2014-7-27-下午3:55:11
 * @param <T> 节点的主键类型
 */
 public static class SearchResult<T> {
 /**
  * 最短路径结果
  */
 List<T> path;
 /**
  * 最短距离
  */
 Integer distance;

 /**
  * 获取实例
  * @param path 最短路径结果
  * @param distance 最短路径距离
  * @return
  */
 protected static <T> SearchResult<T> valueOf(List<T> path, Integer distance) {
  SearchResult<T> r = new SearchResult<T>();
  r.path = path;
  r.distance = distance;
  return r;
 }

 public List<T> getPath() {
  return path;
 }
 public Integer getDistance() {
  return distance;
 }

 @Override
 public String toString() {
  StringBuffer sb = new StringBuffer();
  sb.append("path:");
  for(Iterator<T> it = this.path.iterator(); it.hasNext();) {
  sb.append(it.next());
  if(it.hasNext()) {
   sb.append("->");
  }
  }
  sb.append("\n").append("distance:").append(distance);
  return sb.toString();
 }

 }

 /**
 * 地图对象
 */
 Maps<T> map;
 /**
 * 开始节点
 */
 Maps.Node<T> startNode;
 /**
 * 结束节点
 */
 Maps.Node<T> targetNode;
 /**
 * 开放的节点
 */
 Set<Maps.Node<T>> open = new HashSet<Maps.Node<T>>();
 /**
 * 关闭的节点
 */
 Set<Maps.Node<T>> close = new HashSet<Maps.Node<T>>();
 /**
 * 最短路径距离
 */
 Map<Maps.Node<T>, Integer> path = new HashMap<Maps.Node<T>, Integer>();
 /**
 * 最短路径
 */
 Map<T, List<T>> pathInfo = new HashMap<T, List<T>>();

 /**
 * 初始化起始点
 * <br/>初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"
 * [例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
 * @param source 起始节点的Id
 * @param map 全局地图
 * @param closeSet 已经关闭的节点列表
 * @return
 */
 @SuppressWarnings("unchecked")
 public Maps.Node<T> init(T source, Maps<T> map, Set<T> closeSet) {

 Map<T, Maps.Node<T>> nodeMap = map.getNodes();
 Maps.Node<T> startNode = nodeMap.get(source);
 //将初始节点放到close
 close.add(startNode);
 //将其他节点放到open
 for(Maps.Node<T> node : nodeMap.values()) {
  if(!closeSet.contains(node.getId()) && !node.getId().equals(source)) {
  this.open.add(node);
  }
 }

 // 初始路径
 T startNodeId = startNode.getId();
 for(Entry<Maps.Node<T>, Integer> entry : startNode.getChilds().entrySet()) {
  Maps.Node<T> node = entry.getKey();
  if(open.contains(node)) {
  T nodeId = node.getId();
  path.put(node, entry.getValue());
  pathInfo.put(nodeId, new ArrayList<T>(Arrays.asList(startNodeId, nodeId)));
  }
 }

 for(Maps.Node<T> node : nodeMap.values()) {
  if(open.contains(node) && !path.containsKey(node)) {
  path.put(node, Integer.MAX_VALUE);
  pathInfo.put(node.getId(), new ArrayList<T>(Arrays.asList(startNodeId)));
  }
 }
 this.startNode = startNode;
 this.map = map;
 return startNode;
 }

 /**
 * 递归Dijkstra
 * @param start 已经选取的最近节点
 */
 protected void computePath(Maps.Node<T> start) {
 // 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。
 Maps.Node<T> nearest = getShortestPath(start);
 if (nearest == null) {
  return;
 }
 //更新U中各个顶点到起点s的距离。
 //之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;
 //例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
 close.add(nearest);
 open.remove(nearest);
 //已经找到结果
 if(nearest == this.targetNode) {
  return;
 }
 Map<Maps.Node<T>, Integer> childs = nearest.getChilds();
 for (Maps.Node<T> child : childs.keySet()) {
  if (open.contains(child)) {// 如果子节点在open中
  Integer newCompute = path.get(nearest) + childs.get(child);
  if (path.get(child) > newCompute) {// 之前设置的距离大于新计算出来的距离
   path.put(child, newCompute);

   List<T> path = new ArrayList<T>(pathInfo.get(nearest.getId()));
   path.add(child.getId());
   pathInfo.put(child.getId(), path);
  }
  }
 }
// computePath(start);// 重复执行自己,确保所有子节点被遍历
 computePath(nearest);// 向外一层层递归,直至所有顶点被遍历
 }

 /**
 * 获取与node最近的子节点
 */
 private Maps.Node<T> getShortestPath(Maps.Node<T> node) {
 Maps.Node<T> res = null;
 int minDis = Integer.MAX_VALUE;
 for (Maps.Node<T> entry : path.keySet()) {
  if (open.contains(entry)) {
  int distance = path.get(entry);
  if (distance < minDis) {
   minDis = distance;
   res = entry;
  }
  }
 }
 return res;
 }

 /**
 * 获取到目标点的最短路径
 *
 * @param target
 *      目标点
 * @return
 */
 public SearchResult<T> getResult(T target) {
 Maps.Node<T> targetNode = this.map.getNodes().get(target);
 if(targetNode == null) {
  throw new RuntimeException("目标节点不存在!");
 }
 this.targetNode = targetNode;
 //开始计算
 this.computePath(startNode);
 return SearchResult.valueOf(pathInfo.get(target), path.get(targetNode));
 }

 /**
 * 打印出所有点的最短路径
 */
 public void printPathInfo() {
 Set<Map.Entry<T, List<T>>> pathInfos = pathInfo.entrySet();
 for (Map.Entry<T, List<T>> pathInfo : pathInfos) {
  System.out.println(pathInfo.getKey() + ":" + pathInfo.getValue());
 }
 }

 /**
 * 测试方法
 */
 @org.junit.Test
 public void test() {

 MapBuilder<String> mapBuilder = new Maps.MapBuilder<String>().create();
 //构建节点
 mapBuilder.addNode(Maps.Node.valueOf("A"));
 mapBuilder.addNode(Maps.Node.valueOf("B"));
 mapBuilder.addNode(Maps.Node.valueOf("C"));
 mapBuilder.addNode(Maps.Node.valueOf("D"));
 mapBuilder.addNode(Maps.Node.valueOf("E"));
 mapBuilder.addNode(Maps.Node.valueOf("F"));
 mapBuilder.addNode(Maps.Node.valueOf("G"));
 mapBuilder.addNode(Maps.Node.valueOf("H"));
 mapBuilder.addNode(Maps.Node.valueOf("I"));
 //构建路径
 mapBuilder.addPath("A", "B", 1);
 mapBuilder.addPath("A", "F", 2);
 mapBuilder.addPath("A", "D", 4);
 mapBuilder.addPath("A", "C", 1);
 mapBuilder.addPath("A", "G", 5);
 mapBuilder.addPath("C", "G", 3);
 mapBuilder.addPath("G", "H", 1);
 mapBuilder.addPath("H", "B", 4);
 mapBuilder.addPath("B", "F", 2);
 mapBuilder.addPath("E", "F", 1);
 mapBuilder.addPath("D", "E", 1);
 mapBuilder.addPath("H", "I", 1);
 mapBuilder.addPath("C", "I", 1);

 //构建全局Map
 Maps<String> map = mapBuilder.build();

 //创建路径搜索器(每次搜索都需要创建新的MapSearcher)
 MapSearcher<String> searcher = new MapSearcher<String>();
 //创建关闭节点集合
 Set<String> closeNodeIdsSet = new HashSet<String>();
 closeNodeIdsSet.add("C");
 //设置初始节点
 searcher.init("A", map, closeNodeIdsSet);
 //获取结果
 SearchResult<String> result = searcher.getResult("G");
 System.out.println(result);
 //test.printPathInfo();
 }

}

根据算法的原理可知,getShortestPath是获取open集合里面目前更新的距离离起始点最短路径的节点。基于广度优先原则,可以避免路径权重不均导致错寻的情况。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现Floyd算法求最短路径

    本文实例为大家分享了Java实现Floyd算法求最短路径的具体代码,供大家参考,具体内容如下 import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Scanner; public class TestMainIO { /** * @param args * @throws FileNotFoundException */ public static void main(Stri

  • Java实现利用广度优先遍历(BFS)计算最短路径的方法

    本文实例讲述了Java实现利用广度优先遍历(BFS)计算最短路径的方法.分享给大家供大家参考.具体分析如下: 我们用字符串代表图的顶点(vertax),来模拟学校中Classroom, Square, Toilet, Canteen, South Gate, North Gate几个地点,然后计算任意两点之间的最短路径. 如下图所示: 如,我想从North Gate去Canteen, 程序的输出结果应为: BFS: From [North Gate] to [Canteen]: North Ga

  • java使用Dijkstra算法实现单源最短路径

    单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径.在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质. 一.最短路径的最优子结构性质 该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的中间顶点,那么P(k,s)必定是从k到s的最短路径.下面证明该性质的正确性. 假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,

  • java实现单源最短路径

    本文采用java实现单源最短路径,并带有略微详细的注解,供大家参考,具体内容如下 package com.qf.greaph; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; /** * @author jiayoo * 7 / 30 * Dijkstra最短路径算法是一种单源最短路径 *

  • java实现Dijkstra最短路径算法

    任务描述:在一个无向图中,获取起始节点到所有其他节点的最短路径描述 Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表方式 用OPEN,CLOSE表的方式,其采用的是贪心法的算法策略,大概过程如下: 1.声明两个集合,open和close,open用于存储未遍历的节点,close用来存储

  • java实现最短路径算法之Dijkstra算法

    前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是"贪心算法"的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知识准备: 1.表示图的数据结构 用于存储图的数据结构有多种,本算法中笔者使用的是邻接矩阵. 图的邻接矩阵存储方式是用两个数组来表示图.一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息. 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 从上面可以看出,无

  • Java实现Dijkstra输出最短路径的实例

    Java实现Dijkstra输出指定起点到终点的最短路径 前言: 最近在公司参加了一个比赛,其中涉及的一个问题,可以简化成如是描述:一个二维矩阵,每个点都有权重,需要找出从指定起点到终点的最短路径. 马上就想到了Dijkstra算法,所以又重新温故了一遍,这里给出Java的实现. 而输出最短路径的时候,在网上也进行了查阅,没发现什么标准的方法,于是在下面的实现中,我给出了一种能够想到的比较精简的方式:利用prev[]数组进行递归输出. package graph.dijsktra; import

  • java实现dijkstra最短路径寻路算法

    [引用]迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算). 此外,引进两个集合S和U.S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离). 初始时,S中只有起点s:U中是除s之外的顶点,并且U中顶点的路径是

  • python实现Dijkstra静态寻路算法

    算法介绍 迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树.该算法常用于路由算法或者作为其他图算法的一个子模块. 当然目前也有人将它用来处理物流方面,以获取代价最小的运送方案. 算法思路 Dijkstra算法采用的是一种贪心的策略. 1.首先,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合T. 2.其次,原点 s 的路径权重被赋为 0 (dis[s] = 0).若对于顶点 s 存在能直接到达的边(s,m

  • 教你在 Java 中实现 Dijkstra 最短路算法的方法

    目录 定义 带权有向图的实现 带权有向边 带权有向图 最短路算法 API Dijkstra 算法 算法流程 最小索引优先队列 实现算法 后记 定义 最短路问题的定义为: 下图左侧是一幅带权有向图,以顶点 0 为起点到各个顶点的最短路径形成的最短路径树如下图右侧所示: 带权有向图的实现 在实现最短路算法之前需要先实现带权有向图.在上一篇博客 <如何在 Java 中实现最小生成树算法> 中我们实现了带权无向图,只需一点修改就能实现带权有向图. 带权有向边 首先应该实现带权有向图中的边 Direct

  • Java利用Dijkstra算法求解拓扑关系最短路径

    目录 算法简介 代码实现思路 算法思想 代码示例 算法简介 迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学迪家迪杰斯特拉于1959年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点最短路劲算法,解决的是有权图中最短路径问题.迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止. 代码实现思路 1.先初始化源节点(起始点)到其他各个拓扑节点的最短距离,可以用map存放,key为节点,value为节点到源节点的距

  • Java利用Dijkstra和Floyd分别求取图的最短路径

    目录 1 最短路径的概述 2 杰斯特拉(Dijkstra)算法 2.1 原理 2.2 案例分析 3 弗洛伊德(Floyd)算法 3.1 原理 3.2 案例分析 4 邻接矩阵加权图实现 5 邻接表加权图实现 本文详细介绍了图的最短路径的概念,然后介绍了求最短路径的两种算法:Dijkstra算法和Floyd算法的原理,最后提供了基于邻接矩阵和邻接表的图对两种算法的Java实现. 阅读本文需要一定的图的基础,如果对于图不是太明白的可以看看这篇文章:Java数据结构之图的原理与实现. 1 最短路径的概述

  • 详解Java中Dijkstra(迪杰斯特拉)算法的图解与实现

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

  • Java实现Dijkstra算法的示例代码

    目录 一 问题描述 二 实现 三 测试 一 问题描述 小明为位置1,求他到其他各顶点的距离. 二 实现 package graph.dijkstra; import java.util.Scanner; import java.util.Stack; public class Dijkstra { static final int MaxVnum = 100; // 顶点数最大值 static final int INF = 0x3f3f3f3f; //无穷大 static final int

随机推荐