上記のグラフで幅優先検索を実行して、Node 0
からNode 6
への最短パスを見つけています。
私のコード
public List<Integer> shortestPathBFS(int startNode, int nodeToBeFound){
boolean shortestPathFound = false;
Queue<Integer> queue = new LinkedList<Integer>();
Set<Integer> visitedNodes = new HashSet<Integer>();
List<Integer> shortestPath = new ArrayList<Integer>();
queue.add(startNode);
shortestPath.add(startNode);
while (!queue.isEmpty()) {
int nextNode = queue.peek();
shortestPathFound = (nextNode == nodeToBeFound) ? true : false;
if(shortestPathFound)break;
visitedNodes.add(nextNode);
System.out.println(queue);
Integer unvisitedNode = this.getUnvisitedNode(nextNode, visitedNodes);
if (unvisitedNode != null) {
queue.add(unvisitedNode);
visitedNodes.add(unvisitedNode);
shortestPath.add(nextNode); //Adding the previous node of the visited node
shortestPathFound = (unvisitedNode == nodeToBeFound) ? true : false;
if(shortestPathFound)break;
} else {
queue.poll();
}
}
return shortestPath;
}
BFSアルゴを経由するノードを追跡する必要があります。 [0,3,2,5,6]
のように、ノード6に到達するまでたどった。そのため、shortestPath
という名前のリストを作成し、訪問したノードの以前のノードを保存して、ノードのリストを取得しようとしています。 参照
しかし、それは機能していないようです。最短パスは[0,3,2,5,6]
です
リストにあるのはShortest path: [0, 0, 0, 0, 1, 3, 3, 2, 5]
です
部分的には正しいですが、余分な1
が表示されます。
shortestPath
リストの最初の要素0
から再び開始し、トラバースとバックトラックを開始します。 1
には3
へのエッジがないため、0
から3
から5
にバックトラックして移動すると、答えは得られますが、それが正しい方法かどうか確認してください。
最短経路のノードを取得するための理想的な方法は何ですか?
訪問したすべてのノードを単一のリストに格納しても、最終的にはどのノードがターゲットノードにつながったノードであり、どのノードが行き止まりだったのかを知る方法がないため、最短経路を見つけるのに役立ちません。
あなたがする必要があるのはすべてのノードに対してであり、開始ノードからのパスに前のノードを格納します。
したがって、マップを作成しますMap<Integer, Integer> parentNodes
、これの代わりに:
shortestPath.add(nextNode);
これを行う:
parentNodes.put(unvisitedNode, nextNode);
ターゲットノードに到達したら、そのマップをトラバースして、開始ノードへのパスを見つけることができます。
if(shortestPathFound) {
List<Integer> shortestPath = new ArrayList<>();
Integer node = nodeToBeFound;
while(node != null) {
shortestPath.add(node)
node = parentNodes.get(node);
}
Collections.reverse(shortestPath);
}
あなたが見ることができるように acheron55答え :
「グラフのすべてのエッジが重み付けされていない(または同じ重みである)場合、最初にノードにアクセスしたときに、ソースノードからそのノードへの最短パスになるという非常に便利な特性があります。」
だからあなたがしなければならないすべては、目標が到達されたパスを追跡することです。これを行う簡単な方法は、ノード自体ではなく、ノードに到達するために使用されるパス全体をQueue
にプッシュすることです。
そうすることの利点は、ターゲットに到達したときに、キューがそれに到達するために使用されるパスを保持することです。
これは簡単な実装です:
/**
* unlike common bfs implementation queue does not hold a nodes, but rather collections
* of nodes. each collection represents the path through which a certain node has
* been reached, the node being the last element in that collection
*/
private Queue<List<Node>> queue;
//a collection of visited nodes
private Set<Node> visited;
public boolean bfs(Node node) {
if(node == null){ return false; }
queue = new LinkedList<>(); //initialize queue
visited = new HashSet<>(); //initialize visited log
//a collection to hold the path through which a node has been reached
//the node it self is the last element in that collection
List<Node> pathToNode = new ArrayList<>();
pathToNode.add(node);
queue.add(pathToNode);
while (! queue.isEmpty()) {
pathToNode = queue.poll();
//get node (last element) from queue
node = pathToNode.get(pathToNode.size()-1);
if(isSolved(node)) {
//print path
System.out.println(pathToNode);
return true;
}
//loop over neighbors
for(Node nextNode : getNeighbors(node)){
if(! isVisited(nextNode)) {
//create a new collection representing the path to nextNode
List<Node> pathToNextNode = new ArrayList<>(pathToNode);
pathToNextNode.add(nextNode);
queue.add(pathToNextNode); //add collection to the queue
}
}
}
return false;
}
private List<Node> getNeighbors(Node node) {/* TODO implement*/ return null;}
private boolean isSolved(Node node) {/* TODO implement*/ return false;}
private boolean isVisited(Node node) {
if(visited.contains(node)) { return true;}
visited.add(node);
return false;
}
これは、ノードが複数の親を持つことができる循環グラフにも適用できます。
User3290797によってすでに与えられた回答に加えて。
重み付けされていないグラフを扱っているようです。これをすべてのエッジの重みが1であると解釈します。この場合、ルートノードまでの距離をグラフのすべてのノードに関連付けると(幅優先トラバーサル)、任意の最短パスを再構築するのは簡単になります。ノード、さらには複数のノードがあるかどうかを検出します。
あなたがする必要があるのは、ターゲットノードから開始し、深さの値がちょうど1小さい近傍のみを考慮する、同じグラフの幅(すべての最短パスが必要な場合)または深さ優先のトラバースです。
したがって、距離4(ノード6)から3、2、1、0にジャンプする必要があります。ジャンプする方法は(この場合は)1つだけです。
ノード4への最短経路に関心がある場合、結果は距離2-1-0またはノード4-3-0または4-8-0になります。
ところで、このアプローチは、重み付けされたグラフ(非負の重み)でも機能するように簡単に変更できます。有効な近傍は、現在のエッジからエッジの重みを引いたものに等しい距離を持つものです。最短経路の方が良い場合があります。