您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關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集合里面目前更新的距離離起始點最短路徑的節點。基于廣度優先原則,可以避免路徑權重不均導致錯尋的情況。
關于“java如何實現dijkstra最短路徑尋路算法”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。