必会算法总结(4) - 迪杰斯特拉算法
迪杰斯特拉算法的核心在于贪心+BFS。它和上一篇介绍到的拓扑排序一样属于图论算法,是大学计算机专业必学的算法之一,也是笔试经常出的算法。那么下面让我们一起来学习迪杰斯特拉算法的实现。
算法思想
迪杰斯特拉算法需要维护以下两张列表:
- visited:记录已经计算出最短路径的节点
- shortest:记录到达每个节点的最短路径
每次选取shortest
中的权值最小的节点加入visited
中,并实时维护shortest
列表。
图的存储实现
在本次实例中我选用图的邻接表存储结构:
-
Node类
public class Node { int value; Map<Node, Integer> neighbors; public Node() {} public Node(int value) { this.value = value; } public Node(int value, Map<Node, Integer> neighbors) { this.value = value; neighbors = this.neighbors; } }
-
Graph类
public class Graph { List<Node> nodes; public Graph() {} public Graph(List<Node> nodes) { this.nodes = nodes; } }
算法核心
下列代码是迪杰斯特拉算法的核心,我们使用Integer.MAX_VALUE
来表示无穷大:
public Map<Node, Integer> shortestPath(Graph graph, Node source) {
// 创建列表shortest和visited
Map<Node, Integer> shortest = new HashMap<>();
Set<Node> visited = new HashSet<>();
// 初始化每个node的值为Integer.MAX_VALUE
for (Node node : graph.nodes) {
shortest.put(node, Integer.MAX_VALUE);
}
// 设置源点source
shortest.put(source, 0);
// 选择shortest中的最小值加入visited中,并更新列表shortest
while (true) {
Node node = null;
int min = Integer.MAX_VALUE;
for (Map.Entry<Node, Integer> entry : shortest.entrySet()) {
if (!visited.contains(entry.getKey()) && min > entry.getValue()) {
min = entry.getValue();
node = entry.getKey();
}
}
if (node == null) {
break;
}
visited.add(node);
for (Map.Entry<Node, Integer> entry : node.neighbors.entrySet()) {
if (!visited.contains(entry.getKey())) {
shortest.put(entry.getKey(), Math.min(
shortest.get(entry.getKey()), min + entry.getValue()));
}
}
}
return shortest;
}
总结
下面的代码是上述所有代码的汇总,并附加了一个例子:
public class Main {
static class Node {
int value;
Map<Node, Integer> neighbors;
public Node() {}
public Node(int value) {
this.value = value;
}
public Node(int value, Map<Node, Integer> neighbors) {
this.value = value;
neighbors = this.neighbors;
}
}
static class Graph {
List<Node> nodes;
public Graph() {}
public Graph(List<Node> nodes) {
this.nodes = nodes;
}
}
public Map<Node, Integer> shortestPath(Graph graph, Node source) {
// 创建列表shortest和visited
Map<Node, Integer> shortest = new HashMap<>();
Set<Node> visited = new HashSet<>();
// 初始化每个node的值为Integer.MAX_VALUE
for (Node node : graph.nodes) {
shortest.put(node, Integer.MAX_VALUE);
}
// 设置源点source
shortest.put(source, 0);
// 选择shortest中的最小值加入visited中,并更新列表shortest
while (true) {
Node node = null;
int min = Integer.MAX_VALUE;
for (Map.Entry<Node, Integer> entry : shortest.entrySet()) {
if (!visited.contains(entry.getKey()) && min > entry.getValue()) {
min = entry.getValue();
node = entry.getKey();
}
}
if (node == null) {
break;
}
visited.add(node);
for (Map.Entry<Node, Integer> entry : node.neighbors.entrySet()) {
if (!visited.contains(entry.getKey())) {
shortest.put(entry.getKey(), Math.min(
shortest.get(entry.getKey()), min + entry.getValue()));
}
}
}
return shortest;
}
public static void main(String[] args) {
Node node1 = new Node(1);
Node node2 = new Node(2);
Node node3 = new Node(3);
Node node4 = new Node(4);
Node node5 = new Node(5);
Node node6 = new Node(6);
Node node7 = new Node(7);
node1.neighbors = new HashMap<>();
node2.neighbors = new HashMap<>();
node3.neighbors = new HashMap<>();
node4.neighbors = new HashMap<>();
node5.neighbors = new HashMap<>();
node6.neighbors = new HashMap<>();
node7.neighbors = new HashMap<>();
node1.neighbors.put(node2, 4);
node1.neighbors.put(node3, 6);
node1.neighbors.put(node4, 6);
node2.neighbors.put(node3, 1);
node2.neighbors.put(node5, 7);
node3.neighbors.put(node5, 6);
node3.neighbors.put(node6, 4);
node4.neighbors.put(node3, 2);
node4.neighbors.put(node6, 5);
node5.neighbors.put(node7, 6);
node6.neighbors.put(node5, 1);
node6.neighbors.put(node7, 8);
Graph graph = new Graph(new ArrayList<>(
Arrays.asList(node1, node2, node3, node4, node5, node6, node7)));
System.out.println(shortestPath(graph, node1));
}
}