web-dev-qa-db-fra.com

Recherche des nœuds de chemin les plus courts avec une première recherche étendue

 enter image description here

Je lance une recherche approfondie sur le graphique ci-dessus pour trouver le chemin le plus court allant de Node 0 à Node 6.

Mon code

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;
    }

Je dois localiser les nœuds par lesquels l’algo de BFS. traversé pour atteindre le noeud 6, comme [0,3,2,5,6]. Pour cela, j'ai créé une liste nommée shortestPath & essayant de stocker les noeuds précédents des noeuds visités, pour obtenir la liste des noeuds. Renvoyé

Mais cela ne semble pas fonctionner. Le plus court chemin est [0,3,2,5,6]

Dans la liste, je reçois Shortest path: [0, 0, 0, 0, 1, 3, 3, 2, 5]

C'est partiellement correct mais donne le 1 supplémentaire.

Si je recommence à partir du premier élément 0 de la liste shortestPath & commence à parcourir et revenir en arrière. Comme 1 n'a pas un Edge sur 3, je retourne donc de 0 à 3 à 5, j'obtiendrai la réponse mais ne saurai pas si c'est le bon chemin.

Quel est le moyen idéal pour obtenir les nœuds pour le chemin le plus court?

4
underdog

Stocker tous les nœuds visités dans une seule liste n’est pas utile pour trouver le chemin le plus court car vous n’avez aucun moyen de savoir quels nœuds sont ceux qui mènent au nœud cible et lesquels étaient des impasses.

Ce que vous devez faire est pour chaque nœud pour stocker le nœud précédent dans le chemin depuis le nœud de départ.

Alors, créez un mappage Map<Integer, Integer> parentNodes, et au lieu de ceci:

shortestPath.add(nextNode);

faire ceci:

parentNodes.put(unvisitedNode, nextNode);

Une fois que vous avez atteint le nœud cible, vous pouvez parcourir cette carte pour trouver le chemin du nœud de départ:

if(shortestPathFound) {
    List<Integer> shortestPath = new ArrayList<>();
    Integer node = nodeToBeFound;
    while(node != null) {
        shortestPath.add(node)
        node = parentNodes.get(node);
    }
    Collections.reverse(shortestPath);
}
6
Anton

En plus de la réponse déjà donnée par user3290797.

Il semble que vous ayez affaire à un graphique non pondéré. Nous interprétons cela comme chaque bord a un poids de 1. Dans ce cas, une fois que vous avez associé une distance au nœud racine avec chaque nœud du graphe (le premier parcours en largeur), il devient trivial de reconstruire le plus court chemin noeud, et même détecter s’il y en a plusieurs.

Tout ce que vous avez besoin de faire est une traversée en largeur (au cas où vous voulez chaque chemin le plus court) ou en profondeur du même graphe en partant du nœud cible et en ne considérant que les voisins avec une valeur de profondeur exactement inférieure à 1.  same graph but with distances from node 0

Nous devons donc sauter de la distance 4 (nœud 6) à 3, 2, 1, 0 et il n’ya qu’une façon (dans ce cas) de le faire.

Si le chemin le plus court jusqu'au nœud 4 nous intéresse, le résultat serait les distances 2-1-0 ou les nœuds 4-3-0 ou 4-8-0.

BTW, cette approche peut facilement être modifiée pour fonctionner avec des graphes pondérés (avec des poids non négatifs): les voisins valides sont ceux avec une distance égale au courant moins le poids de l’Edge - cela implique des calculs réels et le stockage direct des nœuds précédents le chemin le plus court pourrait être meilleur.

2
Tymur Gubayev

Comme vous pouvez le voir dans acheron55, répondez :

"Il a la propriété extrêmement utile que si toutes les arêtes d'un graphe ne sont pas pondérées (ou le même poids), alors la première fois qu'un nœud est visité est le chemin le plus court vers ce nœud depuis le nœud source"

Donc, tout ce que vous avez à faire est de garder trace du chemin par lequel la cible a été atteinte. Un moyen simple de le faire est d'insérer dans la Queue le chemin complet utilisé pour atteindre un nœud, plutôt que le nœud lui-même.
L'avantage de le faire est que lorsque la cible est atteinte, la file d'attente contient le chemin utilisé pour l'atteindre.
Voici une implémentation simple:

/**
 * 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;
}

Ceci est également applicable aux graphes cycliques, où un nœud peut avoir plusieurs parents.

0
c0der