简体   繁体   中英

Not able to find shortest path using Dijkstra algorithm?

I am trying to find shrotest path between node a and z using Dijkstra algorithm but everytime it is giving me wrong response.

Below is the code I have -

public class DijkstraAlgorithm {

    // Am I mapping this correctly by looking at the below graph?
    // looks to me I got this wrong?
    private static final Graph.Edge[] GRAPH = { 
        new Graph.Edge("a", "g", 8), 
        new Graph.Edge("a", "b", 1), 
        new Graph.Edge("a", "e", 1), 
        new Graph.Edge("b", "c", 1), 
        new Graph.Edge("b", "e", 1),
        new Graph.Edge("b", "f", 2),
        new Graph.Edge("c", "g", 1),
        new Graph.Edge("c", "d", 1),
        new Graph.Edge("d", "f", 1),
        new Graph.Edge("d", "z", 1),
        new Graph.Edge("e", "f", 4),
        new Graph.Edge("f", "z", 4),
        new Graph.Edge("g", "z", 2),
    };

    private static final String START = "a";
    private static final String END = "z";

    public static void main(String[] args) {
        Graph g = new Graph(GRAPH);
        g.dijkstra(START);
        //  print the shortest path using Dijkstra algorithm
        g.printPath(END);
        //g.printAllPaths();
    }
}

class Graph {
    private final Map<String, Vertex> graph; // mapping of vertex names to Vertex objects, built from a set of Edges

    /** One edge of the graph (only used by Graph constructor) */
    public static class Edge {
        public final String v1, v2;
        public final int dist;

        public Edge(String v1, String v2, int dist) {
            this.v1 = v1;
            this.v2 = v2;
            this.dist = dist;
        }
    }

    /** One vertex of the graph, complete with mappings to neighbouring vertices */
    public static class Vertex implements Comparable<Vertex> {
        public final String name;
        public int dist = Integer.MAX_VALUE; // MAX_VALUE assumed to be infinity
        public Vertex previous = null;
        public final Map<Vertex, Integer> neighbours = new HashMap<Vertex, Integer>();

        public Vertex(String name) {
            this.name = name;
        }

        private void printPath() {
            if (this == this.previous) {
                System.out.printf("%s", this.name);
            } else if (this.previous == null) {
                System.out.printf("%s(unreached)", this.name);
            } else {
                this.previous.printPath();
                System.out.printf(" -> %s(%d)", this.name, this.dist);
            }
        }

        public int compareTo(Vertex other) {
            return Integer.compare(dist, other.dist);
        }
    }

    /** Builds a graph from a set of edges */
    public Graph(Edge[] edges) {
        graph = new HashMap<String, Vertex>(edges.length);

        //one pass to find all vertices
        for (Edge e : edges) {
            if (!graph.containsKey(e.v1))
                graph.put(e.v1, new Vertex(e.v1));
            if (!graph.containsKey(e.v2))
                graph.put(e.v2, new Vertex(e.v2));
        }

        //another pass to set neighbouring vertices
        for (Edge e : edges) {
            graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
            graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist); // also do this for an undirected graph
        }
    }

    /** Runs dijkstra using a specified source vertex */
    public void dijkstra(String startName) {
        if (!graph.containsKey(startName)) {
            System.err.printf("Graph doesn't contain start vertex \"%s\"\n", startName);
            return;
        }
        final Vertex source = graph.get(startName);
        NavigableSet<Vertex> q = new TreeSet<Vertex>();

        // set-up vertices
        for (Vertex v : graph.values()) {
            v.previous = v == source ? source : null;
            v.dist = v == source ? 0 : Integer.MAX_VALUE;
            q.add(v);
        }

        dijkstra(q);
    }

    /** Implementation of dijkstra's algorithm using a binary heap. */
    private void dijkstra(final NavigableSet<Vertex> q) {
        Vertex u, v;
        while (!q.isEmpty()) {

            u = q.pollFirst(); // vertex with shortest distance (first iteration will return source)
            if (u.dist == Integer.MAX_VALUE)
                break; // we can ignore u (and any other remaining vertices) since they are unreachable

            //look at distances to each neighbour
            for (Map.Entry<Vertex, Integer> a : u.neighbours.entrySet()) {
                v = a.getKey(); //the neighbour in this iteration

                final int alternateDist = u.dist + a.getValue();
                if (alternateDist < v.dist) { // shorter path to neighbour found
                    q.remove(v);
                    v.dist = alternateDist;
                    v.previous = u;
                    q.add(v);
                }
            }
        }
    }

    /** Prints a path from the source to the specified vertex */
    public void printPath(String endName) {
        if (!graph.containsKey(endName)) {
            System.err.printf("Graph doesn't contain end vertex \"%s\"\n", endName);
            return;
        }

        graph.get(endName).printPath();
        System.out.println();
    }

    /** Prints the path from the source to every vertex (output order is not guaranteed) */
    public void printAllPaths() {
        for (Vertex v : graph.values()) {
            v.printPath();
            System.out.println();
        }
    }
}

As per the calculation shortest path from node A to node Z is ABCDZ but I am getting A -> E(1) -> F(5) -> D(6) -> Z(7) from my above code which is wrong.

I guess, my mapping of graph data values in the above code is wrong by looking at the graph? Is there any better way I should try to represent my graph?

Is there anything wrong in my above code?

The problem is your queue structure. It is based on a TreeSet which uses your compare function to order things. Unfortunately, a TreeSet can only have a single value for each key, and so if two elements compare equal (eg all the vertices with MAXINT distance will compare equal) they effectively get removed.

You can see this by printing out the length of the queue, you insert 8 elements, but it only gets to be of size 2.

One simple workaround is:

    public int compareTo(Vertex other) {
        if (dist==other.dist)
            return name.compareTo(other.name);
        return Integer.compare(dist, other.dist);
    }

This stops different vertices from being compared as equal.

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