算法练习(19)-单源最短路径dijkstra算法

如上图,先初始化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的最短路径,每转一圈,总路径反而更短。这种情况下,可以将所有边上的权重加“最大负权重”,将所有边上的权重变成非负值。

作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
浙公网安备 33010602011771号