Dijkstra最短路径算法是一种单源最短路径算法,该算法要求路径上任意两点间路径为非负权边。用于计算从路径中指定的顶点到其他所有的顶点的最短路径。所以广泛应用于能够建模为图的问题中,用以查找两个节点最短路径。
算法实现原理
从最小的子路径开始(只包含一个顶点A1),遍历添加其他顶点(Ai)到子路径中,每次重新计算起始点到其他顶点的最短距离(只影响到与Ai相连的顶点),直到所有顶点加入到路径中。
与贪心算法、动态规划比较
Dijkstra算法是个广度搜索的算法。实现的原理与动态规划、贪心算法都有一些共同点,又不完全一样。比较如下:
类同点 | 不同点 | |
贪心算法 | 1、都是从最小的子问题开始递推到整个问题。 2、也用到贪心策略(每一步计算局部最优解为最短路径)。 | 1、可以计算全局最优解,而贪心算法只能计算局部最优解。 2、计算过程中,不会对子问题结果进行纠正。 |
动态规划 | 1、都是从最小的子问题开始递推到整个问题。 | 1、遇到重复子问题,不会重新计算,直接引用结果,同样不会对子问题结果进行纠正。 |
代码实现
以上图为例子,java实现如下:
package algorithm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DijkstraTest {
/**
* 路径中结点名
*/
private static final String[] nodeNameArr = { "A", "B", "C", "D", "E", "F" };
/**
* 路径中各结点到其他结点的路径权矩阵,-1表示不可达
*/
private static final int[][] pathMatrix = { { 0, 6, 3, -1, -1, -1 }, { 6, 0, 2, 5, -1, -1 }, { 3, 2, 0, 3, 4, -1 },
{ -1, 5, 3, 0, 2, 3 }, { -1, -1, 4, 2, 0, 5 }, { -1, -1, -1, 3, 5, 0 } };
public static void main(String[] args) {
Map<String, Node> nodeMap = generateNetwork(nodeNameArr, pathMatrix);
DijkstraPath dijkstraPath = dijkstra(nodeMap, "A");
System.out.println(dijkstraPath);
}
/**
* 根据结点名称,路径矩阵,生成结点网络
*
* @param nodeNameArr
* @param pathMatrix
* @return
*/
public static Map<String, Node> generateNetwork(String[] nodeNameArr, int[][] pathMatrix) {
Node[] nodes = new Node[nodeNameArr.length];
Map<String, Node> nodeMap = new HashMap<>();
for (int i = 0; i < nodeNameArr.length; i++) {
nodes[i] = new Node(nodeNameArr[i]);
nodeMap.put(nodeNameArr[i], nodes[i]);
}
for (int i = 0; i < nodes.length; i++) {
Node node = nodes[i];
int[] nodePath = pathMatrix[i];
Set<Path> pathes = new HashSet<>();
node.setPathes(pathes);
for (int j = 0; j < nodePath.length; j++) {
if (nodePath[j] > 0) {
Path path = new Path();
path.setDistance(nodePath[j]);
path.setStartNode(node);
path.setEndNode(nodes[j]);
pathes.add(path);
}
}
}
return nodeMap;
}
/**
* 在结点网络中,使用dijkstra算法,计算出rNode到其他结点的最小距离
*
* @param nodeMap
* @param rNode
*/
public static DijkstraPath dijkstra(Map<String, Node> nodeMap, String rNode) {
Set<Node> calcSet = new HashSet<>();
Set<Node> restSet = new HashSet<>();
for (Map.Entry<String, Node> entry : nodeMap.entrySet()) {
if (rNode.equals(entry.getKey())) {
calcSet.add(entry.getValue());
} else {
restSet.add(entry.getValue());
}
}
DijkstraPath dijkstraPath = new DijkstraPath(nodeMap.get(rNode));
Map<String, MultiPath> pathMap = new HashMap<>();
for (Node node : restSet) {
MultiPath multiPath = new MultiPath();
multiPath.setDistance(-1);
multiPath.setStartNode(dijkstraPath.getStartNode());
multiPath.setEndNode(node);
multiPath.setPath(new ArrayList<Path>());
pathMap.put(node.getName(), multiPath);
dijkstraPath.addPath(multiPath);
}
// Node node = dijkstraPath.getStartNode();
// Set<Path> pathList = node.getPathes();
calcChildernNodePath(pathMap, null, calcSet);
return dijkstraPath;
}
/**
* 广度遍历处理新加入端点,计算最短路径
* @param pathMap
* @param handledSet
* @param newAddNodes
*/
private static void calcChildernNodePath(Map<String, MultiPath> pathMap, Set<Node> handledSet, Set<Node> newAddNodes) {
if (null == handledSet) {
handledSet = new HashSet<>();
}
if (null == newAddNodes || newAddNodes.size() == 0) {
return;
}
// 遍历处理新加入集合的子结点
for (Node node : newAddNodes) {
MultiPath curMultiPath = pathMap.get(node.getName());
for (Path path : node.getPathes()) {
MultiPath multiPath = pathMap.get(path.getEndNode().getName());
if (null != multiPath) {
if (multiPath.getDistance() <= 0) {// 还未设置最短距离
List<Path> pathes = new ArrayList<>();
if (null == curMultiPath) {//未找到,说明是开始结点,即node=rootNode
multiPath.setDistance(path.getDistance());
} else {
multiPath.setDistance(curMultiPath.getDistance() + path.getDistance());
pathes.addAll(curMultiPath.getPathes());
}
pathes.add(path);
multiPath.setPath(pathes);
} else {
int min = curMultiPath.getDistance() + path.getDistance();
if (min < multiPath.getDistance()) {
List<Path> pathes = new ArrayList<>(curMultiPath.getPathes());
pathes.add(path);
multiPath.setPath(pathes);
multiPath.setDistance(min);
}
}
}
}
handledSet.add(node);
}
Set<Node> nodeSet = new HashSet<>();
for (Node node : newAddNodes) {
// 递归计算子结点
for (Path path : node.getPathes()) {
if (!handledSet.contains(path.getEndNode())) {
nodeSet.add(path.getEndNode());
}
}
}
calcChildernNodePath(pathMap, handledSet, nodeSet);
}
}
class DijkstraPath {
private Node startNode;
private Set<MultiPath> pathSet = new HashSet<>();
public DijkstraPath(Node node) {
this.startNode = node;
}
public Node getStartNode() {
return startNode;
}
public void setStartNode(Node startNode) {
this.startNode = startNode;
}
public Set<MultiPath> getPathSet() {
return pathSet;
}
// public void setPathSet(Set<MultiPath> pathSet) {
// this.pathSet = pathSet;
// }
public void addPath(MultiPath path) {
pathSet.add(path);
}
public void removePath(MultiPath path) {
pathSet.remove(path);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (MultiPath multiPath : pathSet) {
builder.append(multiPath).append('\n');
}
return builder.toString();
}
}
class MultiPath extends Path {
private List<Path> pathes;
public List<Path> getPathes() {
return pathes;
}
public void setPath(List<Path> pathes) {
this.pathes = pathes;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder().append(getStartNode()).append("->").append(getEndNode()).append(":")
.append(getStartNode());
for (Path path : pathes) {
builder.append("->").append(path.getEndNode());
}
return builder.append("[").append(getDistance()).append("]").toString();
}
}
class Node {
private String name;// 结点名
private Set<Path> pathes;// 结点可达路径
public Node(String name) {
super();
this.name = name;
}
public Set<Path> getPathes() {
return pathes;
}
public void setPathes(Set<Path> pathes) {
this.pathes = pathes;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
class Path {
private Node startNode;// 路径起始结点
private Node endNode;// 路径结束结点
private int distance;// 路径权值
public Node getStartNode() {
return startNode;
}
public void setStartNode(Node startNode) {
this.startNode = startNode;
}
public Node getEndNode() {
return endNode;
}
public void setEndNode(Node endNode) {
this.endNode = endNode;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
@Override
public String toString() {
return String.format("%s->%s[%d]", startNode, endNode, distance);
}
}
执行结果:
A->C:A->C[3]
A->B:A->C->B[5]
A->E:A->C->E[7]
A->D:A->C->D[6]
A->F:A->C->D->F[9]