
如上圖,先初始化1個圖,每條邊上的紅色數字為路徑權重:(Node,Edge的定義參見算法練習(17)-圖的廣度優先遍歷/深度優先遍歷)
Graph init() {
List<Node> nodes = new ArrayList<>();
List<Edge> edges = new ArrayList<>();
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
Node n5 = new Node(5);
nodes.add(n1);
nodes.add(n2);
nodes.add(n3);
nodes.add(n4);
nodes.add(n5);
Edge e_1_2 = new Edge(1, n1, n2);
Edge e_2_1 = new Edge(1, n2, n1);
Edge e_1_3 = new Edge(7, n1, n3);
Edge e_3_1 = new Edge(7, n3, n1);
Edge e_1_4 = new Edge(8, n1, n4);
Edge e_4_1 = new Edge(8, n4, n1);
Edge e_2_3 = new Edge(5, n2, n3);
Edge e_3_2 = new Edge(5, n3, n2);
Edge e_3_4 = new Edge(10, n3, n4);
Edge e_4_3 = new Edge(10, n4, n3);
Edge e_2_5 = new Edge(20, n2, n5);
Edge e_5_2 = new Edge(20, n5, n2);
Edge e_5_4 = new Edge(9, n5, n4);
Edge e_4_5 = new Edge(9, n4, n5);
Edge e_3_5 = new Edge(6, n3, n5);
Edge e_5_3 = new Edge(6, n5, n3);
n1.edges.add(e_1_2);
n1.edges.add(e_1_3);
n1.edges.add(e_1_4);
n2.edges.add(e_2_1);
n2.edges.add(e_2_3);
n2.edges.add(e_2_5);
n3.edges.add(e_3_1);
n3.edges.add(e_3_2);
n3.edges.add(e_3_4);
n3.edges.add(e_3_5);
n4.edges.add(e_4_1);
n4.edges.add(e_4_3);
n4.edges.add(e_4_5);
n5.edges.add(e_5_2);
n5.edges.add(e_5_3);
n5.edges.add(e_5_4);
edges.add(e_1_2);
edges.add(e_2_1);
edges.add(e_1_3);
edges.add(e_3_1);
edges.add(e_1_4);
edges.add(e_4_1);
edges.add(e_2_3);
edges.add(e_3_2);
edges.add(e_3_4);
edges.add(e_4_3);
edges.add(e_2_5);
edges.add(e_5_2);
edges.add(e_5_4);
edges.add(e_4_5);
edges.add(e_3_5);
edges.add(e_5_3);
Graph g = new Graph(nodes, edges);
return g;
}
假設從節點1出發,到達其它節點的最短路徑(權重)為:
| 出發點 | 目的地 | 最短路徑(權重)和 | 全路徑 |
| 1 | 1 | 0 | 1->1 |
| 1 | 2 | 1 | 1->2 |
| 1 | 3 | 1+5 | 1->2->3 |
| 1 | 4 | 8 | 1->4 |
| 1 | 5 | 1+5+6 | 1->2->3->5 |
package advanced;
import java.util.*;
public class GraphTest {
Graph init() {
... 略...
}
/**
* dijkstra算法
* @param head
* @return
*/
Map<Node, Integer> dijkstra(Node head) {
/**
* 用於保存從head到其它node的距離總和
* 不在該map中節點,表示還沒走到,默認距離為正無窮
*/
Map<Node, Integer> distanceMap = new HashMap<>();
//首節點:從head到head的距離為0
distanceMap.put(head, 0);
//已經計算過的節點
Set<Node> selectedNodes = new HashSet<>();
//從出發點,找出距離最短的節點
Node minNode = getMinDistanceNode(distanceMap, selectedNodes);
while (minNode != null) {
int distance = distanceMap.get(minNode);
for (Edge edge : minNode.edges) {
Node toNode = edge.to;
if (!distanceMap.containsKey(toNode)) {
distanceMap.put(toNode, distance + edge.weight);
}
//取最短距離,更新distanceMap
distanceMap.put(edge.to, Math.min(distanceMap.get(toNode), distance + edge.weight));
}
//已計算過的節點,做下標識
selectedNodes.add(minNode);
minNode = getMinDistanceNode(distanceMap, selectedNodes);
}
return distanceMap;
}
/**
* 從distanceMap中找出最小距離的節點(已計算過的節點忽略)
* @param distanceMap
* @param touchedNodes
* @return
*/
Node getMinDistanceNode(Map<Node, Integer> distanceMap,
Set<Node> touchedNodes) {
Node minNode = null;
int minDistance = Integer.MAX_VALUE;
for (Map.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;
}
public static void main(String[] args) {
GraphTest t = new GraphTest();
Graph g = t.init();
Map<Node, Integer> dijkstra = t.dijkstra(g.nodes.get(0));
System.out.println(dijkstra);
}
}
輸出:
{3=6, 4=8, 1=0, 5=12, 2=1}
注意:這個算法,有一個前提條件,如果圖中有環,環上的路徑合不能為負值,否則會在環里轉來轉去,每轉一圈,路徑合更小,一直循環,轉不出來。

如上圖,如果從1出發,要計算到節點2的最短路徑,每轉一圈,總路徑反而更短。這種情況下,可以將所有邊上的權重加“最大負權重”,將所有邊上的權重變成非負值。

