【引用】迪杰斯特拉(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集合里面目前更新的距离离起始点最短路径的节点。基于广度优先原则,可以避免路径权重不均导致错寻的情况。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍java实现Dijkstra最短路径算法,包括了java实现Dijkstra最短路径算法的使用技巧和注意事项,需要的朋友参考一下 任务描述:在一个无向图中,获取起始节点到所有其他节点的最短路径描述 Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。 Dijkstra一般的表述
本文向大家介绍java实现最短路径算法之Dijkstra算法,包括了java实现最短路径算法之Dijkstra算法的使用技巧和注意事项,需要的朋友参考一下 前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法。该算法被称为是“贪心算法”的成功典范。本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码。 一、知识准备: 1、表示图的数据结构 用于存储图的
本文向大家介绍Dijkstra算法最短路径的C++实现与输出路径,包括了Dijkstra算法最短路径的C++实现与输出路径的使用技巧和注意事项,需要的朋友参考一下 某个源点到其余各顶点的最短路径 这个算法最开始心里怕怕的,不知道为什么,花了好长时间弄懂了,也写了一遍,又遇到时还是出错了,今天再次写它,心里没那么怕了,耐心研究,懂了之后会好开心的,哈哈 Dijkstra算法: 图G 如图:若要求从顶
我已经从Cormen的第三版参考“算法介绍”中找到的伪代码中实现了Dijkstra算法,用于单源最短路径问题。 我的实现是在python上使用链接列表在邻接列表表示中表示图形。这意味着节点列表是一个链接列表,每个节点都有一个链接列表来表示每个节点的边缘。此外,我没有实现或使用任何二进制堆或斐波那契堆作为算法所需的最小优先级队列,因此当过程需要提取与源距离最小的下一个节点时,我在节点链表内搜索O(V
本文向大家介绍python实现Dijkstra算法的最短路径问题,包括了python实现Dijkstra算法的最短路径问题的使用技巧和注意事项,需要的朋友参考一下 迪杰斯特拉(Dijkstra)算法主要是针对没有负值的有向图,求解其中的单一起点到其他顶点的最短路径算法。 1 算法原理 迪杰斯特拉(Dijkstra)算法是一个按照路径长度递增的次序产生的最短路径算法。下图为带权值的有向图,作为程序中
最短路径问题的Dijkstra算法 是由荷兰计算机科学家艾兹赫尔·戴克斯特拉提出。迪科斯彻算法使用了广度优先搜索解决非负权有向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。 这个算法的python实现途径很多,网上能够发现不少。这里推荐一个我在网上看到的,本来打算自己写,看了这个,决定自己不写了,因为他的已经太好了。 解决(Python) #