未加权图的最短路径(最少节点)
我正在尝试构建一个方法,在未加权的图形中返回从一个节点到另一个节点的最短路径。 我考虑过使用Dijkstra,但这似乎有点矫枉过正,因为我只想要一对。 相反,我已经实现了广度优先搜索,但问题是我的返回列表包含一些我不想要的节点 – 如何修改我的代码以实现我的目标?
public List getDirections(Node start, Node finish){ List directions = new LinkedList(); Queue q = new LinkedList(); Node current = start; q.add(current); while(!q.isEmpty()){ current = q.remove(); directions.add(current); if (current.equals(finish)){ break; }else{ for(Node node : current.getOutNodes()){ if(!q.contains(node)){ q.add(node); } } } } if (!current.equals(finish)){ System.out.println("can't reach destination"); } return directions; }
实际上你的代码不会在循环图中完成,考虑图1 – > 2 – > 1.你必须有一个数组,你可以标记你已访问过哪个节点。 并且对于每个节点,您可以保存之前的节点。 所以这里是正确的代码:
private Map> vis = new HashMap (); private Map prev = new HashMap (); public List getDirections(节点开始,节点完成){ 列出方向= new LinkedList(); 队列q = new LinkedList(); 节点电流=开始; q.add(电流); vis.put(当前,真实); 而(!q.isEmpty()){ current = q.remove(); if(current.equals(finish)){ 打破; }其他{ for(Node node:current.getOutNodes()){ 如果(!vis.contains(节点)){ q.add(节点); vis.put(node,true); prev.put(节点,当前); } } } } if(!current.equals(finish)){ System.out.println(“无法到达目的地”); } for(Node node = finish; node!= null; node = prev.get(node)){ directions.add(节点); } directions.reverse(); 返回方向; }
谢谢Giolekva!
我重写了它,重构了一些:
- 受访节点的集合不必是地图。
- 对于路径重建,可以查找下一个节点,而不是前一个节点,从而无需反转方向。
public List getDirections(Node sourceNode, Node destinationNode) { //Initialization. Map nextNodeMap = new HashMap(); Node currentNode = sourceNode; //Queue Queue queue = new LinkedList (); queue.add(currentNode); /* * The set of visited nodes doesn't have to be a Map, and, since order * is not important, an ordered collection is not needed. HashSet is * fast for add and lookup, if configured properly. */ Set visitedNodes = new HashSet (); visitedNodes.add(currentNode); //Search. while (!queue.isEmpty()) { currentNode = queue.remove(); if (currentNode.equals(destinationNode)) { break; } else { for (Node nextNode : getChildNodes(currentNode)) { if (!visitedNodes.contains(nextNode)) { queue.add(nextNode); visitedNodes.add(nextNode); //Look up of next node instead of previous. nextNodeMap.put(currentNode, nextNode); } } } } //If all nodes are explored and the destination node hasn't been found. if (!currentNode.equals(destinationNode)) { throw new RuntimeException("No feasible path."); } //Reconstruct path. No need to reverse. List directions = new LinkedList (); for (Node node = sourceNode; node != null; node = nextNodeMap.get(node)) { directions.add(node); } return directions; }
获得单对的答案并不比简单的所有对更简单。 计算最短路径的常用方法是像您一样开始,但每当遇到新节点时记录并记录路径上的上一个节点。 然后,当您到达目标节点时,您可以跟踪到源的反向链接并获取路径。 因此,从循环中删除directions.add(current)
,并添加如下代码
Map backlinks = new HashMap();
在开始然后在循环中
if (!backlinks.containsKey(node)) { backlinks.add(node, current); q.add(node); }
然后最后,使用backlinks
映射向后构建directions
列表。
每次通过你的循环,你都会打电话
directions.Add(current);
相反,你应该把它移到你真正知道你想要那个条目的地方。
将它们放入队列时,必须将父节点包含在每个节点中。 然后,您可以递归地从该列表中读取路径。
假设您要在此图中找到从A到D的最短路径:
/B------C------D / | A / \ / \E---------
每次排队节点时,都要跟踪到达此处的方式。 因此,在步骤1 B(A)中,E(A)被放入队列中。 在第二步中,B出队,C(B)被放入队列等。然后通过“向后”递归,很容易找到回来的路。
最好的方法可能是制作一个数组,只要有节点并保持链接在那里(这通常是在Dijkstra’s中完成的)。