简体   繁体   English

使用优先级队列实现 Dijkstra 算法

[英]implementing dijkstra's algorithm using a priority queue

So I'm trying to implement Dijkstra's algorithm.所以我正在尝试实现 Dijkstra 算法。 I understand Dijkstra's works, but I struggle to turn the concept into code.我理解 Dijkstra 的作品,但我很难将概念转化为代码。 I have what I thought would be the correct code, I'm getting an out-of-memory exception on the java heap space, and I'm not sure why.我有我认为正确的代码,我在 java 堆空间上遇到内存不足异常,我不确定为什么。 I also just lost all confidence in my implementation, so any feedback would be great我也对我的实施失去了所有信心,所以任何反馈都会很棒

    protected Path dijkstrasShortestPath(T start, T end) {
        if (start == null || end == null) {
            throw new NoSuchElementException("Vertices cannot have null data");
        }
        if (vertices.get(start) == null || vertices.get(end) == null) {
            throw new NoSuchElementException("Vertices do not exist");
        }
        PriorityQueue<Path> pq = new PriorityQueue<Path>();
        LinkedList<Vertex> visited = new LinkedList<Vertex>();
        Path startPath = new Path(vertices.get(start));
        visited.add(startPath.start);
        pq.add(startPath);
        while (!pq.isEmpty()) {
            Path front = pq.poll();
            visited.add(front.end);
            if (front.end.data.equals(end)) {
                return front;
            } else {
                for (int i = 0; i < front.end.edgesLeaving.size(); i++) {
                    if (!visited.contains(front.end.edgesLeaving.get(i).target)) {
                        pq.add(new Path(front, front.end.edgesLeaving.get(i)));
                    }
                }
            }
        }

        throw new NoSuchElementException("No such path from start to end exists");
    }

these are some other classes and fields that I use这些是我使用的其他一些类和字段

/**
     * Vertex objects group a data field with an adjacency list of weighted
     * directed edges that lead away from them.
     */
    protected class Vertex {
        public T data; // vertex label or application specific data
        public LinkedList<Edge> edgesLeaving;

        public Vertex(T data) {
            this.data = data;
            this.edgesLeaving = new LinkedList<>();
        }
    }

    /**
     * Edge objects are stored within their source vertex, and group together
     * their target destination vertex, along with an integer weight.
     */
    protected class Edge {
        public Vertex target;
        public int weight;

        public Edge(Vertex target, int weight) {
            this.target = target;
            this.weight = weight;
        }
    }

    protected Hashtable<T, Vertex> vertices; // holds graph verticies, key=data

and this is the path class这是路径 class

/**
     * Path objects store a discovered path of vertices and the overall distance of cost
     * of the weighted directed edges along this path. Path objects can be copied and extended
     * to include new edges and vertices using the extend constructor. In comparison to a
     * predecessor table which is sometimes used to implement Dijkstra's algorithm, this
     * eliminates the need for tracing paths backward from the destination vertex to the
     * starting vertex at the end of the algorithm.
     */
    protected class Path implements Comparable<Path> {
        public Vertex start; // first vertex within path
        public int distance; // sumed weight of all edges in path
        public List<T> dataSequence; // ordered sequence of data from vertices in path
        public Vertex end; // last vertex within path

        /**
         * Creates a new path containing a single vertex.  Since this vertex is both
         * the start and end of the path, its initial distance is zero.
         * @param start is the first vertex on this path
         */
        public Path(Vertex start) {
            this.start = start;
            this.distance = 0;
            this.dataSequence = new LinkedList<>();
            this.dataSequence.add(start.data);
            this.end = start;
        }

        /**
         * This extension constructor makes a copy of the path passed into it as an argument
         * without affecting the original path object (copyPath). The path is then extended
         * by the Edge object extendBy.
         * @param copyPath is the path that is being copied
         * @param extendBy is the edge the copied path is extended by
         */
        public Path(Path copyPath, Edge extendBy) {
            this.start = copyPath.start;
            this.start.edgesLeaving.add(extendBy);
            this.distance = extendBy.weight + copyPath.distance;
            this.dataSequence = new LinkedList<>();
            for (int i = 0; i < copyPath.dataSequence.size(); i++) {
                this.dataSequence.add(copyPath.dataSequence.get(i));
            }
            this.end = extendBy.target;
            this.dataSequence.add(end.data);
        }

        /**
         * Allows the natural ordering of paths to be increasing with path distance.
         * When path distance is equal, the string comparison of end vertex data is used to break ties.
         * @param other is the other path that is being compared to this one
         * @return -1 when this path has a smaller distance than the other,
         *         +1 when this path has a larger distance than the other,
         *         and the comparison of end vertex data in string form when these distances are tied
         */
        public int compareTo(Path other) {
            int cmp = this.distance - other.distance;
            if(cmp != 0) return cmp; // use path distance as the natural ordering
            // when path distances are equal, break ties by comparing the string
            // representation of data in the end vertex of each path
            return this.end.data.toString().compareTo(other.end.data.toString());
        }
    }

I didn't look deeply into the code and I don't remember how dijkstra works, but it seems to me very suspicious that you do not have equals and hashCode defined for your classes.我没有深入研究代码,也不记得dijkstra是如何工作的,但在我看来,您没有为类定义equalshashCode非常可疑。 I didn't find where you may use it but maybe I've overlooked.我没有找到你可以在哪里使用它,但也许我忽略了。

Also you have this line: if (front.end.data.equals(end)) {你也有这一行: if (front.end.data.equals(end)) {
Here we may have a problem in case when T does not have equals overload.如果T没有equals重载,我们可能会遇到问题。 I'm not sure, which type you use for data, so it may or may not be a problem.我不确定,您使用哪种类型的数据,所以它可能是也可能不是问题。

I think the problem is caused by this.start.edgesLeaving.add(extendBy) in the Path(Path, Edge) constructor.我认为问题是由Path(Path, Edge)构造函数中的this.start.edgesLeaving.add(extendBy)引起的。 In the first iteration of the while loop, a path consisting of just the start vertex is considered.在 while 循环的第一次迭代中,考虑了仅由起始顶点组成的路径。 In the for loop, the algorithm then iterates over all edges leaving from the end vertex of that path, where the end vertex and the start vertex are equal.在 for 循环中,算法然后迭代从该路径的结束顶点离开的所有边,其中结束顶点和开始顶点相等。 For every of these leaving edges, a new path is created with the Path(Path, Edge) constructor and every time, a new element is added to the edgesLeaving list of the start vertex.对于这些离开边中的每一个,都会使用Path(Path, Edge)构造函数创建一条新路径,并且每次都会将一个新元素添加到起始顶点的edgesLeaving列表中。 This causes the value of front.end.edgesLeaving.size() to grow by one element, so the execution never leaves the for loop and on every iteration, edgesLeaving grows by one, eventually exhausting heap space.这导致front.end.edgesLeaving.size()的值增长一个元素,因此执行永远不会离开 for 循环,并且在每次迭代中, edgesLeaving增长一个,最终耗尽堆空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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