import java.util.*; /** * Dijkstras算法實現。 * * Character改成String。 * bug: 終點沒有定義時,空指針異常。解決方法:終點定義一個空的Vertex便可。 * 不足:初始化Graph時,必須按節點來分組。(但初始化已分組好,效率更高) * * @author mburst * @author danni * @since 2019.7.16 */ public class Dijkstras { static Graph g = null; private static void initGraph() { g = new Graph(); } public static Graph getGraph() { return g; } public static void setGraph(String vertex, List<Vertex> vertexList) { if (g == null) initGraph(); g.addVertex(vertex, vertexList); } public static List<String> getShortestPath(String src, String dest) { List<String> l = g.getShortestPath(src, dest); if (!l.isEmpty()) l.add(src); Collections.reverse(l);// 倒序 return l; } } @SuppressWarnings("Duplicates") class Graph { private final Map<String, List<Vertex>> vertices; public Graph() { this.vertices = new HashMap<>(); } public void addVertex(String str, List<Vertex> vertexList) { this.vertices.put(str, vertexList); } /** * 把頂點集合V分紅兩組: * (1)S:已求出的頂點的集合(初始時只含有源點V0) * (2)V-S=T:還沒有肯定的頂點集合 * * 將T中頂點按遞增的次序加入到S中,保證: * (1)從源點V0到S中其餘各頂點的長度都不大於從V0到T中任何頂點的最短路徑長度 * (2)每一個頂點對應一個距離值 * * https://baike.baidu.com/item/迪傑斯特拉算法 */ public List<String> getShortestPath(String start, String finish) { final Map<String, Integer> distances = new HashMap<>();// 權重(距離) final Map<String, Vertex> previous = new HashMap<>(); PriorityQueue<Vertex> nodes = new PriorityQueue<>();// 頂點的集合(初始時只含有源點V0的距離爲0,其他均爲inf) // 遍歷所有頂點集合,初始化距離,源點爲0,其餘爲正無窮(2147483647) for (String vertex : vertices.keySet()) { if (vertex == start) {// 源點 distances.put(vertex, 0); nodes.add(new Vertex(vertex, 0));// 初始時只含有源點V0的距離爲0 } else {// 非源點 distances.put(vertex, Integer.MAX_VALUE); nodes.add(new Vertex(vertex, Integer.MAX_VALUE));// 其他均爲inf } previous.put(vertex, null);// 目前只有源點,所以前一個頂點先設置爲null } while (!nodes.isEmpty()) { // 最短距離的頂點 Vertex smallest = nodes.poll();// 默認狀況下PriorityQueue使用天然排序法,最小元素先出列(offer入列,poll出列) // 若是最短距離的頂點爲目標頂點,則表示已求得最短路徑,直接返回 if (smallest.getId() == finish) { final List<String> path = new ArrayList<>(); // 循環把最短路徑的每個節點排列出來,放到path裏,而後返回path while (previous.get(smallest.getId()) != null) { path.add(smallest.getId()); smallest = previous.get(smallest.getId());// 取前一個頂點 } return path; } // 若是最短的距離是正無窮,則不用計算了,直接退出就能夠了(什麼時候這種狀況?) if (distances.get(smallest.getId()) == Integer.MAX_VALUE) { break; } for (Vertex neighbor : vertices.get(smallest.getId())) { // alt = 源點與當前(最短路徑)頂點的距離 + 當前頂點與它每個相鄰頂點的距離 Integer alt = distances.get(smallest.getId()) + neighbor.getDistance(); // System.out.println(neighbor.getId()); // System.out.println(distances.get(neighbor.getId())); // System.out.println(alt); // 把alt與distances中保存的該頂點距離做比較,若是alt更小,則更新爲alt,讓distances始終保持裏面的距離是較小的,最終會最小 if (alt < distances.get(neighbor.getId())) {// 若是沒定義終點,這裏distances.get會爲null,與alt比較會空指針異常 distances.put(neighbor.getId(), alt); previous.put(neighbor.getId(), smallest); // 更新該頂點在集合中的距離值 forloop: for (Vertex n : nodes) { if (n.getId() == neighbor.getId()) { nodes.remove(n);// 先把頂點從集合作剔除 n.setDistance(alt); nodes.add(n);// 再把該頂點加到集合,實際上是爲了修改該頂點的距離值 break forloop; } } } } } // 終點不存在的狀況(會break),會返回所有,bug //return new ArrayList<>(distances.keySet()); // 修改成返回空 return new ArrayList<>(); } } @SuppressWarnings("Duplicates") public class Vertex implements Comparable<Vertex> { private String id; private Integer distance; public Vertex(String id, Integer distance) { super(); this.id = id; this.distance = distance; } public String getId() { return id; } public Integer getDistance() { return distance; } public void setId(String id) { this.id = id; } public void setDistance(Integer distance) { this.distance = distance; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((distance == null) ? 0 : distance.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Vertex other = (Vertex) obj; if (distance == null) { if (other.distance != null) return false; } else if (!distance.equals(other.distance)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; return true; } @Override public String toString() { return "Vertex [id=" + id + ", distance=" + distance + "]"; } @Override public int compareTo(Vertex o) { if (this.distance < o.distance) return -1; else if (this.distance > o.distance) return 1; else return this.getId().compareTo(o.getId()); } } import java.util.Arrays; public class Test { public static void main(String[] args) { initTest(); System.out.println(Dijkstras.getShortestPath("A", "C")); System.out.println(Dijkstras.getShortestPath("A", "G")); System.out.println(Dijkstras.getShortestPath("A", "F")); System.out.println(Dijkstras.getShortestPath("A", "D")); System.out.println(Dijkstras.getShortestPath("A", "E"));// E並不存在 System.out.println(Dijkstras.getShortestPath("A", "H"));// 沒法達到H } static void testMotor() { init(); System.out.println(Dijkstras.getShortestPath("1234500100001000", "1234500100001410")); System.out.println(Dijkstras.getShortestPath("1234500100001410", "1234500100001000")); } private static void init() { Graph g = Dijkstras.g; g.addVertex("1234500100001000", Arrays.asList(new Vertex("1234500100001210", 0))); g.addVertex("1234500100001210", Arrays.asList(new Vertex("1234500100001200", 7400))); g.addVertex("1234500100001200", Arrays.asList(new Vertex("1234500100001410", 0))); g.addVertex("1234500100001410", Arrays.asList()); } private static void initTest() { Graph g = Dijkstras.g; g.addVertex("A", Arrays.asList(new Vertex("B", 7), new Vertex("C", 8))); g.addVertex("B", Arrays.asList(new Vertex("C", 8), new Vertex("F", 2))); g.addVertex("C", Arrays.asList(new Vertex("F", 6), new Vertex("G", 4))); g.addVertex("D", Arrays.asList(new Vertex("F", 8))); g.addVertex("F", Arrays.asList(new Vertex("C", 6), new Vertex("G", 9), new Vertex("D", 8))); g.addVertex("G", Arrays.asList(new Vertex("C", 4), new Vertex("F", 9))); g.addVertex("H", Arrays.asList()); } }