使用Java在Dijkstra中k个最短路径的无向图

时间:2017-12-31 12:53:53

标签: java shortest-path dijkstra

我试图实现Dijkstra算法来计算java中的k最短路径到目前为止,这里使用的代码是:

import java.util.List;


public interface AbstractKShortestPathFinder<V> {

    List<Path<V>> findShortestPaths(V source, V target, Graph<V> graph, int `k);`

    default void checkK(int k) {
        if (k < 1) {
            throw new IllegalArgumentException(
                    String.format("The value of k is too small: %d, should `be at least 1.", k));`
        }
    }
}

-

import java.util.*;

import static java.util.Objects.requireNonNull;

public class DefaultKShortestPathFinder<V> implements AbstractKShortestPathFinder<V> {

    @Override
    public List<Path<V>> findShortestPaths(V source, V target, Graph<V> graph, int k) {
        requireNonNull(source, "The source node is null.");
        requireNonNull(target, "The target node is null.");
        requireNonNull(graph, "The graph is null.");
        checkK(k);

        List<Path<V>> paths = new ArrayList<>(k);
        Map<V, Integer> countMap = new HashMap<>();
        Queue<Path<V>> HEAP = new PriorityQueue<>(
                Comparator.comparingDouble(Path::pathCost));

        HEAP.add(new Path<>(source));

        while (!HEAP.isEmpty() && countMap.getOrDefault(target, 0) < k) {
            Path<V> currentPath = HEAP.remove();
            V endNode = currentPath.getEndNode();

            countMap.put(endNode, countMap.getOrDefault(endNode, 0) + 1);

            if (endNode.equals(target)) {
                paths.add(currentPath);
            }

            if (countMap.get(endNode) <= k) {
                for (Edge<V> edge : graph.get(endNode)) {
                    Path<V> path = currentPath.append(edge);
                    HEAP.add(path);
                }
            }
        }

        return paths;
    }
}

-

public class Edge<V> {

    public final V from;
    public final V to;
    public final double weight;


    public Edge(V from, V to, double weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
        if (Double.isNaN(weight)) {
            throw new IllegalArgumentException("The weight is NaN.");
        }
        if (weight < 0.0) {
            throw new IllegalArgumentException("The weight is negative.");
        }
    }

}

-

import java.util.*;

import static java.lang.String.*;

public class Graph<V> {

    //could be replaced by http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html
    private Map<V,Map<V,Edge<V>>> vertexEdgeMap = new HashMap<>();

    @SafeVarargs
    public Graph(Edge<V> ... edges) {
        for (Edge<V> edge : edges) {
            addEdge(edge);
        }
    }

    private void addEdge(Edge<V> edge) {
        vertexEdgeMap.putIfAbsent(edge.from, new HashMap<>());
        Map<V, Edge<V>> fromMap = vertexEdgeMap.get(edge.from);
        if(fromMap.containsKey(edge.to)) {
            throw new IllegalArgumentException(format("Edge between %s and %s was added twice", edge.from, edge.to));
        }
        fromMap.put(edge.to, edge);
    }

    public Edge<V> get(V from, V to) {
        return vertexEdgeMap.get(from).get(to);
    }

    public Collection<Edge<V>> get(V from) {
        return vertexEdgeMap.getOrDefault(from, Collections.emptyMap()).values();
    }

}

-

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import static java.lang.String.format;

public class Path<V> {

    private final V node;
    private final double totalCost;

    public Path(V source) {
        Objects.requireNonNull(source, "The input source node is null.");
        node = source;
        totalCost = 0.0;
    }

    private Path(V node, double totalCost) {
        this.node = node;
        this.totalCost = totalCost;
    }


    public Path<V> append(Edge<V> edge) {
        if (!node.equals(edge.from)) {
            throw new IllegalArgumentException(format("The edge %s doesn't extend the path %s", edge, this.getNodeList()));
        }

        return new NonEmptyPath<>(this, edge);
    }

    public V getEndNode() {
        return node;
    }

    public List<V> getNodeList() {
        return new ArrayList<>();
    }

    public double pathCost() {
        return totalCost;
    }

    private static class NonEmptyPath<V> extends Path<V> {
        private final Path<V> predecessor;

        public NonEmptyPath(Path<V> path, Edge<V> edge) {
            super(edge.to, path.totalCost + edge.weight);
            predecessor = path;

        }

        @Override
        public List<V> getNodeList() {
            LinkedList<V> result = new LinkedList<>();
            Path<V> path = this;
            while(path instanceof NonEmptyPath) {
                result.addFirst(path.node);
                path = ((NonEmptyPath<V>) path).predecessor;
            }
            result.addFirst(path.node);
            return result;
        }

        @Override
        public String toString() {
            return getNodeList().toString();
        }
    }

}

-

import java.util.List;

public class Execution {

    public static void main(String[] args) {
        execution();
    }

    private static void execution() {

        Graph<Character> graph = new Graph<>(
               // new Edge<>('a', 'b', 5.0),
                new Edge<>('a', 'c', 3.0),
                new Edge<>('b', 'c', 2.0),
                new Edge<>('b', 'd', 1.0),
                new Edge<>('c', 'd', 3.0)


        );

        List<Path<Character>> paths = new DefaultKShortestPathFinder<Character>().findShortestPaths('a', 'b', graph, 2);



        for(Path<Character> path:paths) {
            System.out.println('h');
            System.out.println(path.toString() + " cout: " + path.pathCost());
        }

    }
}

一切都适用于有向图,但是我想修改它以便它可以计算无向图的k最短路径,任何人都知道如何实现它,我也知道对于相邻矩阵中的有向图[ a] [b] =真,但[b] [a] =假。

1 个答案:

答案 0 :(得分:0)

无向图只是有向图,其中对于每个边a-> b,还存在边b-> a。只需将无向图转换为有向图并使用现有算法即可。