/** * dijkstra 单元最短路径算法 */// no negative weightpublic class Code06_Dijkstra { /** * 方式1.傻白甜版本 * 给一个节点,返回该点所能到达该点的最小路径和,到达不了的为正无穷 */ public static HashMap<Node, Integer> dijkstra1(Node from) { // 距离表 HashMap<Node, Integer> distanceMap = new HashMap<>(); // 到自己的距离为0 distanceMap.put(from, 0); // 打过对号的点 HashSet<Node> selectedNodes = new HashSet<>(); // 获取距离map表中未被选择最短据距离的节点,第一步调用必定是from,原始节点 Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); while (minNode != null) { // 原始点 -> minNode(跳转点) 原始点from到中转点minNode的最小距离distance int distance = distanceMap.get(minNode); // 考虑跳转点的所有边 for (Edge edge : minNode.edges) { Node toNode = edge.to; if (!distanceMap.containsKey(toNode)) { // toNode 不在distanceMap中,认为距离为正无穷 distanceMap.put(toNode, distance + edge.weight); } else { // toNode 在distanceMap中进行比较,小的更新 distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight)); } } // 这个中转点结束 selectedNodes.add(minNode); // 获取下一个中转点 minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes); } return distanceMap; } // 查找距离表中除被选择的节点之外的距离最短的节点 public static Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap, HashSet<Node> touchedNodes) { Node minNode = null; int minDistance = Integer.MAX_VALUE; for (Entry<Node, Integer> entry : distanceMap.entrySet()) { Node node = entry.getKey(); int distance = entry.getValue(); if (!touchedNodes.contains(node) && distance < minDistance) { minNode = node; minDistance = distance; } } return minNode; } // ################################以下是dijkstra优化方法################################################### /** * 方法2:优化,对每次查找距离表中的最小距离进行优化,getMinDistanceAndUnselectedNode这个方法。加强堆 * @param head * @param size 图的节点个数 * @return */ // 改进后的dijkstra算法 // 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回 public static HashMap<Node, Integer> dijkstra2(Node head, int size) { NodeHeap nodeHeap = new NodeHeap(size); // 堆中 add update ignore 整合在一起 nodeHeap.addOrUpdateOrIgnore(head, 0); // logN HashMap<Node, Integer> result = new HashMap<>(); while (!nodeHeap.isEmpty()) { NodeRecord record = nodeHeap.pop(); Node cur = record.node; int distance = record.distance; for (Edge edge : cur.edges) { nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance); } result.put(cur, distance); } return result; } // 存放在加强堆中的节点封装,节点和当前到节点的距离 public static class NodeRecord { public Node node; public int distance; public NodeRecord(Node node, int distance) { this.node = node; this.distance = distance; } } // 加强堆 小根堆结构描述 public static class NodeHeap { private Node[] nodes; // 正向索引,实际的堆结构 private HashMap<Node, Integer> heapIndexMap; // 反向索引表,根据节点找到节点所在的位置,key存节点value存放node的下标 private HashMap<Node, Integer> distanceMap; // key 某一个节点, value 从源节点出发到该节点的目前最小距离 private int size; // 堆上有多少个点 public NodeHeap(int size) { nodes = new Node[size]; heapIndexMap = new HashMap<>(); distanceMap = new HashMap<>(); size = 0; } public boolean isEmpty() { return size == 0; } // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance // 判断要不要更新,如果需要的话,就更新 public void addOrUpdateOrIgnore(Node node, int distance) { if (inHeap(node)) { // 如果在堆上,距离小进行更新 distanceMap.put(node, Math.min(distanceMap.get(node), distance)); insertHeapify(node, heapIndexMap.get(node)); // 向上调整 } if (!isEntered(node)) { // 表示没有进来过,表示堆中新增一个节点 nodes[size] = node; heapIndexMap.put(node, size); distanceMap.put(node, distance); insertHeapify(node, size++); } // 既没有在堆上也不是第一次进堆,ignore操作,不做任何操作。 } // 弹出堆顶元素 public NodeRecord pop() { NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0])); swap(0, size - 1); heapIndexMap.put(nodes[size - 1], -1); distanceMap.remove(nodes[size - 1]); // free C++同学还要把原本堆顶节点析构,对java同学不必 nodes[size - 1] = null; heapify(0, --size); return nodeRecord; } private void insertHeapify(Node node, int index) { while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) { swap(index, (index - 1) / 2); index = (index - 1) / 2; } } private void heapify(int index, int size) { int left = index * 2 + 1; while (left < size) { int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left]) ? left + 1 : left; smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index; if (smallest == index) { break; } swap(smallest, index); index = smallest; left = index * 2 + 1; } } // 判断一个节点是否进去过堆 private boolean isEntered(Node node) { return heapIndexMap.containsKey(node); } // headIndexMap value值为-1 表示node进出过依次堆,为已经找到最小路径的节点,不在操纵。 private boolean inHeap(Node node) { return isEntered(node) && heapIndexMap.get(node) != -1; } // 堆交换的同时保持反向索引表同步更新 private void swap(int index1, int index2) { heapIndexMap.put(nodes[index1], index2); heapIndexMap.put(nodes[index2], index1); Node tmp = nodes[index1]; nodes[index1] = nodes[index2]; nodes[index2] = tmp; } }}