Dijkstra最短路径算法

        Dijkstra最短路径算法是一种单源最短路径算法,该算法要求路径上任意两点间路径为非负权边。用于计算从路径中指定的顶点到其他所有的顶点的最短路径。所以广泛应用于能够建模为图的问题中,用以查找两个节点最短路径。

  
算法实现原理
        从最小的子路径开始(只包含一个顶点A1),遍历添加其他顶点(Ai)到子路径中,每次重新计算起始点到其他顶点的最短距离(只影响到与Ai相连的顶点),直到所有顶点加入到路径中。
  
与贪心算法、动态规划比较
  Dijkstra算法是个广度搜索的算法。实现的原理与动态规划、贪心算法都有一些共同点,又不完全一样。比较如下:


  类同点 不同点
贪心算法 1、都是从最小的子问题开始递推到整个问题。
2、也用到贪心策略(每一步计算局部最优解为最短路径)。
1、可以计算全局最优解,而贪心算法只能计算局部最优解。
2、计算过程中,不会对子问题结果进行纠正。
动态规划 1、都是从最小的子问题开始递推到整个问题。 1、遇到重复子问题,不会重新计算,直接引用结果,同样不会对子问题结果进行纠正。


代码实现

《Dijkstra最短路径算法》

以上图为例子,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]

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/lanxiangru/article/details/78367595
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞