Commit solutions to h07
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
src/main/java/h07
|
||||
|
||||
### VS-Code ###
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
96
src/main/java/h07/algorithm/Dijkstra.java
Normal file
96
src/main/java/h07/algorithm/Dijkstra.java
Normal file
@ -0,0 +1,96 @@
|
||||
package h07.algorithm;
|
||||
|
||||
import h07.algebra.Monoid;
|
||||
import h07.graph.DirectedGraph;
|
||||
import h07.graph.Path;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Dijkstra<V, A> implements ShortestPathsAlgorithm<V, A> {
|
||||
|
||||
@Override
|
||||
public Map<V, Path<V, A>> shortestPaths(DirectedGraph<V, A> graph, V startNode, Monoid<A> monoid, Comparator<? super A> comparator) {
|
||||
var d = new DijkstraImpl(graph, startNode, monoid, comparator);
|
||||
d.computeShortestPaths();
|
||||
return d.paths;
|
||||
}
|
||||
|
||||
private class DijkstraImpl {
|
||||
final DirectedGraph<V, A> graph;
|
||||
final V startNode;
|
||||
final Monoid<A> monoid;
|
||||
final Comparator<? super A> comparator;
|
||||
|
||||
final Map<V, Path<V, A>> paths;
|
||||
final Queue<V> queue;
|
||||
|
||||
private DijkstraImpl(DirectedGraph<V, A> graph, V startNode, Monoid<A> monoid, Comparator<? super A> comparator) {
|
||||
this.graph = Objects.requireNonNull(graph, "Argument graph may not be null");
|
||||
this.startNode = Objects.requireNonNull(startNode, "Argument startNode may not be null");
|
||||
this.monoid = Objects.requireNonNull(monoid, "Argument monoid may not be null");
|
||||
this.comparator = Objects.requireNonNull(comparator, "Argument comparator may not be null");
|
||||
|
||||
if (!graph.getAllNodes().contains(startNode)) {
|
||||
throw new NoSuchElementException("Node startNode not in graph");
|
||||
}
|
||||
|
||||
this.paths = getPathMap();
|
||||
this.queue = getQueue();
|
||||
}
|
||||
|
||||
private HashMap<V, Path<V, A>> getPathMap() {
|
||||
var paths = new HashMap<V, Path<V, A>>();
|
||||
paths.put(startNode, Path.of(startNode));
|
||||
return paths;
|
||||
}
|
||||
|
||||
private PriorityQueue<V> getQueue() {
|
||||
var queue = new PriorityQueue<V>(Comparator.comparing(n ->
|
||||
sumPathDistances(paths.get(n)), comparator));
|
||||
queue.add(startNode);
|
||||
return queue;
|
||||
}
|
||||
|
||||
private A sumPathDistances(Path<?, A> path) {
|
||||
return Stream.iterate(
|
||||
path.traverser(),
|
||||
Path.Traverser::hasNextNode,
|
||||
t -> { t.walkToNextNode(); return t; })
|
||||
.map(Path.Traverser::getDistanceToNextNode)
|
||||
.reduce(monoid.zero(), monoid::add);
|
||||
}
|
||||
|
||||
public void computeShortestPaths() {
|
||||
while (!queue.isEmpty()) {
|
||||
var node = queue.remove();
|
||||
|
||||
for (var child : graph.getChildrenForNode(node)) {
|
||||
var path = getPath(node, child);
|
||||
|
||||
if (isShortestPath(child, path)) {
|
||||
paths.put(child, path);
|
||||
queue.remove(child);
|
||||
queue.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isShortestPath(V child, Path<V, A> path) {
|
||||
if (!paths.containsKey(child)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 0 > comparator.compare(
|
||||
sumPathDistances(path),
|
||||
sumPathDistances(paths.get(child)));
|
||||
}
|
||||
|
||||
private Path<V, A> getPath(V node, V child) {
|
||||
return paths
|
||||
.get(node)
|
||||
.concat(child, graph.getArcWeightBetween(node, child));
|
||||
}
|
||||
}
|
||||
}
|
91
src/main/java/h07/graph/DirectedGraphImpl.java
Normal file
91
src/main/java/h07/graph/DirectedGraphImpl.java
Normal file
@ -0,0 +1,91 @@
|
||||
package h07.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class DirectedGraphImpl<V, A> implements DirectedGraph<V, A> {
|
||||
|
||||
private final Map<V, Map<V, A>> theGraph;
|
||||
|
||||
DirectedGraphImpl() {
|
||||
theGraph = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getAllNodes() {
|
||||
return Set.copyOf(theGraph.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> getChildrenForNode(V node) {
|
||||
return Set.copyOf(getEdges(node).keySet());
|
||||
}
|
||||
|
||||
private Map<V, A> getEdges(V node) {
|
||||
var edges = theGraph.get(Objects.requireNonNull(node));
|
||||
if (edges == null) {
|
||||
throw new NoSuchElementException("Node not in graph: " + node);
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getArcWeightBetween(V from, V to) {
|
||||
checkToNode(to);
|
||||
|
||||
var arc = getEdges(from).get(to);
|
||||
if (arc == null) {
|
||||
throw new NoSuchElementException("No edge from " + from + " to " + to);
|
||||
}
|
||||
return arc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNode(V node) {
|
||||
Objects.requireNonNull(node);
|
||||
if (theGraph.containsKey(node)) {
|
||||
throw new IllegalArgumentException("Node already exists in graph: " + node);
|
||||
}
|
||||
theGraph.put(node, new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(V node) {
|
||||
Objects.requireNonNull(node);
|
||||
if (theGraph.remove(node) == null) {
|
||||
throw new NoSuchElementException("Node does not exist in graph: " + node);
|
||||
}
|
||||
for (var edges : theGraph.values()) {
|
||||
edges.remove(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectNodes(V from, A weight, V to) {
|
||||
Objects.requireNonNull(from);
|
||||
Objects.requireNonNull(weight);
|
||||
checkToNode(to);
|
||||
var edges = getEdges(from);
|
||||
if (edges.containsKey(to)) {
|
||||
throw new IllegalArgumentException("Edge from " + from + " to " + to + " already exists");
|
||||
}
|
||||
edges.put(to, weight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnectNodes(V from, V to) {
|
||||
Objects.requireNonNull(from);
|
||||
checkToNode(to);
|
||||
|
||||
var edges = getEdges(from);
|
||||
if (edges.remove(to) == null) {
|
||||
throw new NoSuchElementException("No edge from " + from + " to " + to);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkToNode(V to) {
|
||||
Objects.requireNonNull(to);
|
||||
if (!theGraph.containsKey(to)) {
|
||||
throw new NoSuchElementException("Node not in graph: " + to);
|
||||
}
|
||||
}
|
||||
}
|
@ -95,11 +95,8 @@ public interface Path<V, A> extends Iterable<V> {
|
||||
* @throws NullPointerException falls der Knoten {@code null} ist
|
||||
*/
|
||||
static <V, A> Path<V, A> of(V v1) {
|
||||
throw new UnsupportedOperationException("Noch nicht implementiert.");
|
||||
/*
|
||||
Objects.requireNonNull(v1, "Der Knoten eines Pfades darf nicht null sein");
|
||||
return new PathImpl<>(v1);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
|
120
src/main/java/h07/graph/PathImpl.java
Normal file
120
src/main/java/h07/graph/PathImpl.java
Normal file
@ -0,0 +1,120 @@
|
||||
package h07.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class PathImpl<V, A> implements Path<V, A> {
|
||||
|
||||
private final List<Node> thePath;
|
||||
|
||||
public PathImpl(V v1) {
|
||||
Objects.requireNonNull(v1, "Argument v1 may not be null");
|
||||
this.thePath = List.of(new Node(v1, null));
|
||||
}
|
||||
|
||||
public PathImpl(List<Node> thePath) {
|
||||
this.thePath = thePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Traverser<V, A> traverser() {
|
||||
return new TraverserImp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path<V, A> concat(V node, A distance) {
|
||||
Objects.requireNonNull(node, "Argument node may not be null");
|
||||
Objects.requireNonNull(distance, "Argument distance may not be null");
|
||||
|
||||
var list = new ArrayList<>(thePath);
|
||||
list.add(new Node(node, distance));
|
||||
return new PathImpl<V, A>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
var iter = thePath.iterator();
|
||||
return new Iterator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V next() {
|
||||
return iter.next().node;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var sb = new StringBuilder();
|
||||
var trav = traverser();
|
||||
|
||||
while(trav.hasNextNode()) {
|
||||
sb.append(trav.getCurrentNode())
|
||||
.append(" -[")
|
||||
.append(trav.getDistanceToNextNode())
|
||||
.append("]-> ");
|
||||
trav.walkToNextNode();
|
||||
}
|
||||
|
||||
return sb
|
||||
.append(trav.getCurrentNode())
|
||||
.toString();
|
||||
}
|
||||
|
||||
private class Node {
|
||||
final V node;
|
||||
final A distance;
|
||||
|
||||
private Node(V node, A distance) {
|
||||
this.node = node;
|
||||
this.distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
private class TraverserImp implements Traverser<V, A> {
|
||||
Node current;
|
||||
Node next;
|
||||
Iterator<Node> iter;
|
||||
|
||||
TraverserImp() {
|
||||
this.iter = thePath.iterator();
|
||||
if (iter.hasNext()) {
|
||||
current = iter.next();
|
||||
}
|
||||
if (iter.hasNext()) {
|
||||
next = iter.next();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getCurrentNode() {
|
||||
return current.node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public A getDistanceToNextNode() {
|
||||
if (next == null) {
|
||||
throw new IllegalStateException("No next node present");
|
||||
}
|
||||
return next.distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void walkToNextNode() {
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException("No next node present");
|
||||
}
|
||||
|
||||
current = next;
|
||||
next = iter.hasNext() ? iter.next() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNextNode() {
|
||||
return next != null;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user