Commit solutions to h07
This commit is contained in:
		
							parent
							
								
									37c78cf5a5
								
							
						
					
					
						commit
						5c4ce8e3af
					
				
					 5 changed files with 307 additions and 5 deletions
				
			
		
							
								
								
									
										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 a new issue