2009-10-16 15 views
11

Estoy intentando crear un método que devuelva la ruta más corta de un nodo a otro en un gráfico no ponderado. Consideré el uso de Dijkstra, pero esto parece un poco exagerado ya que solo quiero un par. En su lugar, he implementado una búsqueda de amplitud, pero el problema es que mi lista de devolución contiene algunos de los nodos que no quiero, ¿cómo puedo modificar mi código para lograr mi objetivo?Ruta más corta (menos nodos) para gráficos no ponderados

public List<Node> getDirections(Node start, Node finish){ 
    List<Node> directions = new LinkedList<Node>(); 
    Queue<Node> q = new LinkedList<Node>(); 
    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; 
} 
+0

¿Por qué no se desea que algunos de esos nodos? – mkoryak

+0

no todos ellos pertenecen a una sola ruta de ruta más corta – Robert

+0

¿El nodo de clase sobrescribe equal y hashcode correctamente? – mkoryak

Respuesta

20

En realidad, su código no terminará en gráficos cíclicos, considere el gráfico 1 -> 2 -> 1. Debe tener alguna matriz donde puede marcar qué nodo ha visitado ya. Y también para cada nodo puede guardar nodos previos, de donde proviene. Así que aquí está el código correcto:

 
private Map<Node, Boolean>> vis = new HashMap<Node, Boolean>(); 

private Map<Node, Node> prev = new HashMap<Node, Node>(); 

public List getDirections(Node start, Node finish){ 
    List directions = new LinkedList(); 
    Queue q = new LinkedList(); 
    Node current = start; 
    q.add(current); 
    vis.put(current, true); 
    while(!q.isEmpty()){ 
     current = q.remove(); 
     if (current.equals(finish)){ 
      break; 
     }else{ 
      for(Node node : current.getOutNodes()){ 
       if(!vis.contains(node)){ 
        q.add(node); 
        vis.put(node, true); 
        prev.put(node, current); 
       } 
      } 
     } 
    } 
    if (!current.equals(finish)){ 
     System.out.println("can't reach destination"); 
    } 
    for(Node node = finish; node != null; node = prev.get(node)) { 
     directions.add(node); 
    } 
    directions.reverse(); 
    return directions; 
} 
+0

te refieres a vis.get (nodo) == nulo, por supuesto, de lo contrario hay una excepción de puntero nulo – Robert

+0

sí, ya lo he cambiado con el método contains. He escrito ese código aquí sin ningún IDE, por lo que podría haber algunos errores tipográficos :) – giolekva

+0

este es un muy buen ejemplo - finalmente hizo clic para mí de cómo encontrar la ruta más corta y también los pasos – KumarM

0

Cada vez que a través de su bucle, que llaman

directions.Add(current); 

su lugar, debe mover eso a un lugar donde realmente sabe que quiere que la entrada.

+0

¿cómo se puede saber si es apropiado agregarlo o no? – Robert

1

Realmente no es más sencillo obtener la respuesta para un solo par que para todos los pares. La forma habitual de calcular una ruta más corta es comenzar como lo hace, pero tome nota siempre que encuentre un nuevo nodo y grabe el nodo anterior en la ruta. Luego, cuando llegue al nodo de destino, puede seguir los enlaces de retroceso hasta la fuente y obtener la ruta. Por lo tanto, eliminar el directions.add(current) del bucle, y agregar código similar al siguiente

Map<Node,Node> backlinks = new HashMap<Node,Node>(); 

en el comienzo y luego en el bucle

if (!backlinks.containsKey(node)) { 
    backlinks.add(node, current); 
    q.add(node); 
} 

y luego, al final, acaba de construir la lista directions en hacia atrás usando el mapa backlinks.

0

Debe incluir el nodo padre en cada nodo cuando los coloca en su cola. Luego puede leer recursivamente la ruta desde esa lista.

decir que quiere encontrar el camino más corto desde la A a la D en este gráfico:

 /B------C------D 
/    | 
A    /
    \   /
    \E--------- 

Cada vez que un nodo de encolar, no perder de vista la forma de llegar aquí. Por lo tanto, en el paso 1 B (A) E (A) se pone en la cola. En el paso dos, B se quita de la cola y C (B) se pone en la cola, etc. Entonces es fácil encontrar el camino de regreso, simplemente repitiendo "hacia atrás".

La mejor manera es, probablemente, hacer una matriz, siempre y cuando haya nodos y mantener los enlaces allí, (que normalmente se hace, por ejemplo, en Dijkstra).

4

Gracias Giolekva!

me volvió a escribir, algunos refactorización:

  • La colección de nodos visitados no tiene que ser un mapa.
  • Para la reconstrucción de ruta, el siguiente nodo podría buscarse, en lugar del nodo anterior, eliminando la necesidad de invertir las direcciones.
public List<Node> getDirections(Node sourceNode, Node destinationNode) { 
    //Initialization. 
    Map<Node, Node> nextNodeMap = new HashMap<Node, Node>(); 
    Node currentNode = sourceNode; 

    //Queue 
    Queue<Node> queue = new LinkedList<Node>(); 
    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<Node> visitedNodes = new HashSet<Node>(); 
    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<Node> directions = new LinkedList<Node>(); 
    for (Node node = sourceNode; node != null; node = nextNodeMap.get(node)) { 
     directions.add(node); 
    } 

    return directions; 
} 
+1

Este método es incorrecto porque para el próximo ejemplo el resultado será malo. Para los siguientes pares1-2 1-3 2-5 getDirections ("1", "5") = "1", "3" ' – Smoggit

Cuestiones relacionadas