1. /**
    2. * dijkstra 单元最短路径算法
    3. */
    4. // no negative weight
    5. public class Code06_Dijkstra {
    6. /**
    7. * 方式1.傻白甜版本
    8. * 给一个节点,返回该点所能到达该点的最小路径和,到达不了的为正无穷
    9. */
    10. public static HashMap<Node, Integer> dijkstra1(Node from) {
    11. // 距离表
    12. HashMap<Node, Integer> distanceMap = new HashMap<>();
    13. // 到自己的距离为0
    14. distanceMap.put(from, 0);
    15. // 打过对号的点
    16. HashSet<Node> selectedNodes = new HashSet<>();
    17. // 获取距离map表中未被选择最短据距离的节点,第一步调用必定是from,原始节点
    18. Node minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
    19. while (minNode != null) {
    20. // 原始点 -> minNode(跳转点) 原始点from到中转点minNode的最小距离distance
    21. int distance = distanceMap.get(minNode);
    22. // 考虑跳转点的所有边
    23. for (Edge edge : minNode.edges) {
    24. Node toNode = edge.to;
    25. if (!distanceMap.containsKey(toNode)) { // toNode 不在distanceMap中,认为距离为正无穷
    26. distanceMap.put(toNode, distance + edge.weight);
    27. } else { // toNode 在distanceMap中进行比较,小的更新
    28. distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
    29. }
    30. }
    31. // 这个中转点结束
    32. selectedNodes.add(minNode);
    33. // 获取下一个中转点
    34. minNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
    35. }
    36. return distanceMap;
    37. }
    38. // 查找距离表中除被选择的节点之外的距离最短的节点
    39. public static Node getMinDistanceAndUnselectedNode(HashMap<Node, Integer> distanceMap, HashSet<Node> touchedNodes) {
    40. Node minNode = null;
    41. int minDistance = Integer.MAX_VALUE;
    42. for (Entry<Node, Integer> entry : distanceMap.entrySet()) {
    43. Node node = entry.getKey();
    44. int distance = entry.getValue();
    45. if (!touchedNodes.contains(node) && distance < minDistance) {
    46. minNode = node;
    47. minDistance = distance;
    48. }
    49. }
    50. return minNode;
    51. }
    52. // ################################以下是dijkstra优化方法###################################################
    53. /**
    54. * 方法2:优化,对每次查找距离表中的最小距离进行优化,getMinDistanceAndUnselectedNode这个方法。加强堆
    55. * @param head
    56. * @param size 图的节点个数
    57. * @return
    58. */
    59. // 改进后的dijkstra算法
    60. // 从head出发,所有head能到达的节点,生成到达每个节点的最小路径记录并返回
    61. public static HashMap<Node, Integer> dijkstra2(Node head, int size) {
    62. NodeHeap nodeHeap = new NodeHeap(size);
    63. // 堆中 add update ignore 整合在一起
    64. nodeHeap.addOrUpdateOrIgnore(head, 0); // logN
    65. HashMap<Node, Integer> result = new HashMap<>();
    66. while (!nodeHeap.isEmpty()) {
    67. NodeRecord record = nodeHeap.pop();
    68. Node cur = record.node;
    69. int distance = record.distance;
    70. for (Edge edge : cur.edges) {
    71. nodeHeap.addOrUpdateOrIgnore(edge.to, edge.weight + distance);
    72. }
    73. result.put(cur, distance);
    74. }
    75. return result;
    76. }
    77. // 存放在加强堆中的节点封装,节点和当前到节点的距离
    78. public static class NodeRecord {
    79. public Node node;
    80. public int distance;
    81. public NodeRecord(Node node, int distance) {
    82. this.node = node;
    83. this.distance = distance;
    84. }
    85. }
    86. // 加强堆 小根堆结构描述
    87. public static class NodeHeap {
    88. private Node[] nodes; // 正向索引,实际的堆结构
    89. private HashMap<Node, Integer> heapIndexMap; // 反向索引表,根据节点找到节点所在的位置,key存节点value存放node的下标
    90. private HashMap<Node, Integer> distanceMap; // key 某一个节点, value 从源节点出发到该节点的目前最小距离
    91. private int size; // 堆上有多少个点
    92. public NodeHeap(int size) {
    93. nodes = new Node[size];
    94. heapIndexMap = new HashMap<>();
    95. distanceMap = new HashMap<>();
    96. size = 0;
    97. }
    98. public boolean isEmpty() {
    99. return size == 0;
    100. }
    101. // 有一个点叫node,现在发现了一个从源节点出发到达node的距离为distance
    102. // 判断要不要更新,如果需要的话,就更新
    103. public void addOrUpdateOrIgnore(Node node, int distance) {
    104. if (inHeap(node)) { // 如果在堆上,距离小进行更新
    105. distanceMap.put(node, Math.min(distanceMap.get(node), distance));
    106. insertHeapify(node, heapIndexMap.get(node)); // 向上调整
    107. }
    108. if (!isEntered(node)) { // 表示没有进来过,表示堆中新增一个节点
    109. nodes[size] = node;
    110. heapIndexMap.put(node, size);
    111. distanceMap.put(node, distance);
    112. insertHeapify(node, size++);
    113. }
    114. // 既没有在堆上也不是第一次进堆,ignore操作,不做任何操作。
    115. }
    116. // 弹出堆顶元素
    117. public NodeRecord pop() {
    118. NodeRecord nodeRecord = new NodeRecord(nodes[0], distanceMap.get(nodes[0]));
    119. swap(0, size - 1);
    120. heapIndexMap.put(nodes[size - 1], -1);
    121. distanceMap.remove(nodes[size - 1]);
    122. // free C++同学还要把原本堆顶节点析构,对java同学不必
    123. nodes[size - 1] = null;
    124. heapify(0, --size);
    125. return nodeRecord;
    126. }
    127. private void insertHeapify(Node node, int index) {
    128. while (distanceMap.get(nodes[index]) < distanceMap.get(nodes[(index - 1) / 2])) {
    129. swap(index, (index - 1) / 2);
    130. index = (index - 1) / 2;
    131. }
    132. }
    133. private void heapify(int index, int size) {
    134. int left = index * 2 + 1;
    135. while (left < size) {
    136. int smallest = left + 1 < size && distanceMap.get(nodes[left + 1]) < distanceMap.get(nodes[left])
    137. ? left + 1
    138. : left;
    139. smallest = distanceMap.get(nodes[smallest]) < distanceMap.get(nodes[index]) ? smallest : index;
    140. if (smallest == index) {
    141. break;
    142. }
    143. swap(smallest, index);
    144. index = smallest;
    145. left = index * 2 + 1;
    146. }
    147. }
    148. // 判断一个节点是否进去过堆
    149. private boolean isEntered(Node node) {
    150. return heapIndexMap.containsKey(node);
    151. }
    152. // headIndexMap value值为-1 表示node进出过依次堆,为已经找到最小路径的节点,不在操纵。
    153. private boolean inHeap(Node node) {
    154. return isEntered(node) && heapIndexMap.get(node) != -1;
    155. }
    156. // 堆交换的同时保持反向索引表同步更新
    157. private void swap(int index1, int index2) {
    158. heapIndexMap.put(nodes[index1], index2);
    159. heapIndexMap.put(nodes[index2], index1);
    160. Node tmp = nodes[index1];
    161. nodes[index1] = nodes[index2];
    162. nodes[index2] = tmp;
    163. }
    164. }
    165. }