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()); } }