当前位置: 移动技术网 > IT编程>开发语言>Java > java实现dijkstra最短路径寻路算法

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

2019年07月19日  | 移动技术网IT编程  | 我要评论

【引用】迪杰斯特拉(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集合里面目前更新的距离离起始点最短路径的节点。基于广度优先原则,可以避免路径权重不均导致错寻的情况。

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网