Commit solutions to h07

This commit is contained in:
2021-09-05 22:42:44 +02:00
parent 37c78cf5a5
commit 5c4ce8e3af
5 changed files with 307 additions and 5 deletions

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
src/main/java/h07
### VS-Code ###
.vscode/
*.code-workspace

View 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));
}
}
}

View 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);
}
}
}

View File

@ -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);
*/
}
/**

View 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;
}
}
}