简体   繁体   中英

Use Dijkstra's algorithm to get shortest path when distance are as coordinates

I had a looked at other posts regarding this here at SO.

I'm trying to find the shortest path between nodes in a graph. Each edge between nodes has an (X, Y) coordinate.

I want to calculate the shortest distance from Node I to Node J . Once have that I want to add up all the X values and Y values from the coordinates in shortest path.

I have been stuck at this for hours and would love some insight.

Here is the code:

class Vertex implements Comparable<Vertex> {
    private int id;
    private List<Edge> adjacencyList;
    private Vertex previousVertex;
    private double minDistance;
    private Coordinate point;

    public Vertex(int id, Coordinate point) {
        this.id = id;
        this.point = point;
        this.adjacencyList = new ArrayList<>();
    }

    public int getID() {
        return this.id;
    }

    public Coordinate getPoint() {
        return this.point;
    }

    public List<Edge> getAdjacencyList() {
        return this.adjacencyList;
    }

    public void addNeighbour(Edge edge) {
        this.adjacencyList.add(edge);
    }

    public Vertex getPreviousVertex() {
        return this.previousVertex;
    }

    public void setPreviousVertex(Vertex previousVertex) {
        this.previousVertex = previousVertex;
    }

    public double getMinDistance() {
        return this.minDistance;
    }

    public void setMinDistance(double minDistance) {
        this.minDistance = minDistance;
    }

    public int compareTo(Vertex other) {
        return Double.compare(this.minDistance, other.minDistance);
    }
}


class Edge {
    private double weight;
    private Vertex targetVertex;

    public Edge(double weight, Vertex targetVertex) {
        this.weight = weight;
        this.targetVertex = targetVertex;
    }

    public double getWeight() {
        return this.weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    public Vertex getTargetVertex() {
        return this.targetVertex;
    }

    public void setTargetVertex(Vertex targetVertex) {
        this.targetVertex = targetVertex;
    }
}

class Algorithm {
    public void shortestPath(Vertex startVertex) {
        startVertex.setMinDistance(0);
        PriorityQueue<Vertex> queue = new PriorityQueue<>();
        queue.add(startVertex);

        while (!queue.isEmpty()) {
            Vertex actualVertex = queue.poll();

            for (Edge edge : actualVertex.getAdjacencyList()) {
                Vertex v = edge.getTargetVertex();
                double weight = edge.getWeight();               
                double currentDistance = actualVertex.getMinDistance() + weight;

                if (currentDistance < v.getMinDistance()) {
                    queue.remove(v);
                    v.setMinDistance(currentDistance);
                    v.setPreviousVertex(actualVertex);
                    queue.add(v);
                }
            }
        }
    }

    public List<Vertex> getShortestPathTo(Vertex targetVertex){
        List<Vertex> path = new ArrayList<Vertex>();
        for (Vertex vertex = targetVertex; vertex != null; vertex = vertex.getPreviousVertex()){
            path.add(vertex);
        }
        Collections.reverse(path);
        return path;    
    }
}

class Coordinate {
    private int x;
    private int y;

    Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return this.x;
    } 

    public int getY() {
        return this.y;
    }

    public static Coordinate readInput(Scanner in) {
       String[] temp = in.nextLine().split(" ");
       return new Coordinate(Integer.parseInt(temp[0]), Integer.parseInt(temp[1]));
    }
}

If I read from this textfile

3 //# of coordinates

0 0 // (x, y)

1 1 // (x, y)

2 0 // (x, y)

1 2 //edge between coordinate 1 to 2

2 3 //edge between coordinate 2 to 3

My test case would look like this:

class Test {
    public static void main(String[] args) {

        Scanner s = new Scanner(System.in);
        String[] constants = s.nextLine().split(" ");

        final int N = Integer.parseInt(constants[0]);

        List<Coordinate> junctions = new ArrayList<>();
        List<Coordinate> paths = new ArrayList<>();
        List<Vertex> vertices = new ArrayList<>();

        for(int i = 0; i < N; i++) {
            junctions.add(Coordinate.readInput(s));
        }

        for(int i = 0; i < N-1; i++) {
            paths.add(Coordinate.readInput(s));
        }

        for(int i = 0; i < N-1; i++) {
            int x = junctions.get(paths.get(i).getX() - 1).getX();
            int x1 = junctions.get(paths.get(i).getY() - 1).getX();
            int y = junctions.get(paths.get(i).getX() - 1).getY();
            int y1 = junctions.get(paths.get(i).getY() - 1).getY();

            Vertex vertex1 = new Vertex(paths.get(i).getX(), new Coordinate(x, y));
            Vertex vertex2 = new Vertex(paths.get(i).getY(), new Coordinate(x1, y1));

            double distance = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));

            vertex1.addNeighbour(new Edge(distance, vertex2));

            vertices.add(vertex1);
            vertices.add(vertex2);
        }

        Algorithm a = new Algorithm();
        int x = 0;
        int y = 0;

        for(int i = 0; i < vertices.size(); i++) {
            a.shortestPath(vertices.get(i));

            for(Vertex vertex : a.getShortestPathTo(vertices.get(i+1))) {
                x += vertices.get(vertex.getID()).getPoint().getX();
                y += vertices.get(vertex.getID()).getPoint().getY();
            }

        }
        //This prints out "Total X: 5 Total Y: 3" (should be 3 and 1)
        System.out.println("Total X: " + x + " Total Y: " + y);
    }
}

To solve this problem you need to keep track for each node, what is the parent of that node in the Dijktra's tree. Once you keep track of that you are able to recover the shortest path recursively, traverse it and calculate what you need to know.

Your problem is in this part:

public void shortestPath(Vertex startVertex) {
    startVertex.setMinDistance(0);
    PriorityQueue<Vertex> queue = new PriorityQueue<>();
    queue.add(startVertex);
    //The rest is omitted
}

Each time you run the shortestPath method, you should reset all the minDistance in all vertexes to infinity, not just the startVertex .

For all vertexes except the startVertex , the minDistance at the beginning should be set to infinity (or Double.MAX_VALUE ) , or it will always be 0 .

Code:

for(Vertex v : vertices){
    v.setMinDistance(Double.MAX_VALUE);
    v.setPreviousVertex(null);  
}
a.shortestPath(vertices.get(i));

Further more, in your third loop of the class Test , you initialize same vertex more than one. So, what you should do is, pre-initializing all the vertexes and keep them in an array like this:

for(int i = 0; i < N; i++){
    vertices.add(new Vertex(i + 1, junctions.get(i)); 
}

for(int i = 0; i < N - 1; i++){
    //Initialize x, y, x1, y1 here, I just skipped it
    Vertex vertex1 = vertices.get(paths.getX() - 1);
    Vertex vertex2 = vertices.get(paths.getY() - 1);

    double distance = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2));

    vertex1.addNeighbour(new Edge(distance, vertex2));


}

As promised, I have created a github repo for my bipartite graph solver. You can take a look here: Bipartite Solver on GitHub

As you explained, you have a set of points you are trying to find the minimum distance between. You have a whole bunch of X and Y coordinates, and you are attempting to find the smallest total euclidean distance between nodes.

The algorithm you are most interested in is Jonker-Volgenant, which implements Dijkstra's Algorithm for traversing nodes.

The library includes a bunch of standalone executables you can run to test the code.

Let me know if this is useful to you.

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