简体   繁体   中英

Finding shortest path between two nodes

I am trying to figure out how to make a method which finds the shortest way between two nodes, but cant seem to figure it out. I was given two files, one file containing actors (which will be the nodes) and one with movies (which will be the edges).

I have represented a graph as a HashMap where the key is a actor, and the value is a ArrayList with the movies that actor has played in:

HashMap<Actor, ArrayList[Movie]> graph;

Now i wanna find the shortest way between two actors but i dont know how to. I was thinking DFS or BFS but im not quite sure how to do that with this hashmap..

You can use your current structure ( HashMap<Actor, ArrayList<Movie>> ) as an adjacency list, but there is something missing: an efficient way to go from movies back to actors that starred in them. You can build a Map<Movie, List<Actor>> , and that would work... but it is simpler to use a single adjacency list, instead of having one for actors and another for movies. I would do as follows (assuming that Actor and Movie both have equals and hashcode implemented, and no actor equals any movie):

 Map<Object, List<Object>> edges = new HashMap<>();

 for (Map.Entry<Actor, ArrayList<Movie>> e : graph.entries()) {
     Actor a = e.getKey();         
     edges.put(a, new ArrayList<Object>(e.getValues()));
     for (Movie m : e.getValues()) {
         List<Actor> otherActors = edges.getOrDefault(m, new ArrayList<Object>());
         otherActors.add(a);
         edges.put(m, otherActors);
     }
 }

Now you can use edges to perform a standard breadth-first search. It is guaranteed to give you a shortest path (there may be multiple equally-short paths) between any 2 actors, and the path will contain the movies that link them.

It's not the entire solution just to point you in the direction, you will have to create a Map<Movie, List> as tucuxi suggested . You traverse every actor one by one and mark the distance on the current actor from your reference actor.

private static void fn(HashMap<Actor, List<Movie>> graph, Actor referenceActor) {
    var movieMap = new HashMap<Movie, List<Actor>>();
    graph.forEach((key, value) -> {
        for (var movie : value) {
            movieMap.compute(movie, (k, v) -> {
                if (v == null) {
                    return new ArrayList<>(List.of(key));
                }
                v.add(key);
                return v;
            });
        }
    });


    final var visited = new HashSet<Actor>();
    var dq = new ArrayDeque<Actor>();
    var map = new HashMap<Actor, Integer>();

    dq.add(referenceActor);
    int distance = 0;
    while (!dq.isEmpty()) {
        var size = dq.size();
        for (int i = 0; i < size; i++) {
            var actor = dq.poll();
            visited.add(actor);
            map.putIfAbsent(actor, distance);
            for (Movie movie : graph.get(actor)) {
                // adding every non visited actor in the same movie in the queue
                dq.addAll(movieMap
                        .get(movie)
                        .stream()
                        .filter(x -> !visited.contains(x))
                        .collect(Collectors.toList()));
            }
        }
        distance++;
    }
    System.out.println(map);
}

I used ArrayDeque here to traverse in BFS fashion you can mend if you want DFS bear in mind the logic to calculate distance will also need to be changed. Pasting the image from the comments , taking node B as reference actor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM