Compare commits
10 Commits
7e44d0fb3f
...
master
Author | SHA1 | Date | |
---|---|---|---|
0d8459c6f7 | |||
40c576280e | |||
32c23e115c | |||
6d4a8b26e6 | |||
f1303fe37b | |||
d8b5a76fb0 | |||
5c4ce8e3af | |||
37c78cf5a5 | |||
da5b0fd5b3 | |||
bb7b48e6e6 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
src/main/java/h07
|
||||
|
||||
### VS-Code ###
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
@ -45,6 +45,15 @@ Startknoten (grün) auswählen löscht Pfäde.
|
||||
- Escape, `Q`:
|
||||
Beenden
|
||||
|
||||
- `U`, `I`:
|
||||
Gewichtung für die nächste Kante verkleinern/vergrößern
|
||||
|
||||
- `O`:
|
||||
Gewichtung für die nächste Kante auf 1 zurücksetzten
|
||||
|
||||
- `F`:
|
||||
Berechne oder verstecke den minimalen Spannbaum (blau)
|
||||
|
||||
## Wenn Knoten ausgewählt (rot)
|
||||
|
||||
- WASD:
|
||||
|
@ -75,6 +75,19 @@ public class Canvas extends JPanel {
|
||||
r.run();
|
||||
g.scale(1/scale, 1/scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void text(double x, double y, String text) {
|
||||
FontMetrics metrics = g.getFontMetrics(g.getFont());
|
||||
int d = metrics.getAscent();
|
||||
g.drawString(text, round(x), round(y) + d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void textSize(int size) {
|
||||
var f = g.getFont();
|
||||
g.setFont(new Font(f.getName(), Font.PLAIN, size));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -23,4 +23,8 @@ public interface Drawable {
|
||||
void translated(double x, double y, Runnable r);
|
||||
|
||||
void scaled(double scale, Runnable r);
|
||||
|
||||
void text(double x, double y, String text);
|
||||
|
||||
void textSize(int size);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package de.oshgnacknak.gruphi;
|
||||
|
||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
||||
import h07.algorithm.ShortestPathsAlgorithm;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
@ -38,4 +39,9 @@ public interface Gruphi {
|
||||
* @return A {@link ShortestPathsAlgorithm} for the path finding
|
||||
*/
|
||||
ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm();
|
||||
|
||||
/**
|
||||
* @return A {@link MinimumSpanningForestAlgorithm} for the spanning tree calculation
|
||||
*/
|
||||
MinimumSpanningForestAlgorithm<Node, Double> getMinimumSpanningForestAlgorithm();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package de.oshgnacknak.gruphi;
|
||||
|
||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
||||
import h07.algorithm.ShortestPathsAlgorithm;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
@ -12,6 +13,7 @@ public class GruphiBuilder {
|
||||
private BiPredicate<Node, Node> neighbourPredicate;
|
||||
private DirectedGraphFactory<Node, Double> directedGraphFactory;
|
||||
private ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
||||
private MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm;
|
||||
|
||||
public GruphiBuilder setFrameDelay(Long frameDelay) {
|
||||
this.frameDelay = frameDelay;
|
||||
@ -43,7 +45,12 @@ public class GruphiBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public GruphiBuilder setMinimumSpanningForestAlgorithm(MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm) {
|
||||
this.minimumSpanningForestAlgorithm = minimumSpanningForestAlgorithm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GruphiImpl createGruphi() {
|
||||
return new GruphiImpl(frameDelay, velocity, gridSpacing, neighbourPredicate, directedGraphFactory, shortestPathsAlgorithm);
|
||||
return new GruphiImpl(frameDelay, velocity, gridSpacing, neighbourPredicate, directedGraphFactory, shortestPathsAlgorithm, minimumSpanningForestAlgorithm);
|
||||
}
|
||||
}
|
@ -31,6 +31,8 @@ class GruphiFrame extends JFrame {
|
||||
|
||||
private final DirectedGraph<Node, Double> graph;
|
||||
|
||||
private DirectedGraph<Node, Double> forest;
|
||||
|
||||
private final MazeGenerator<Node> mazeGenerator;
|
||||
private final Gruphi gruphi;
|
||||
|
||||
@ -44,6 +46,7 @@ class GruphiFrame extends JFrame {
|
||||
private Map<Node, Path<Node, Double>> paths = null;
|
||||
private boolean running = true;
|
||||
private boolean nuggets = false;
|
||||
private double weight = 1.0;
|
||||
|
||||
GruphiFrame(Gruphi gruphi) {
|
||||
super("Gruphi - The Graph GUI - By Osh");
|
||||
@ -111,6 +114,7 @@ class GruphiFrame extends JFrame {
|
||||
}
|
||||
graph.removeNode(selected);
|
||||
selected = null;
|
||||
forest = null;
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -141,6 +145,22 @@ class GruphiFrame extends JFrame {
|
||||
generatePaths();
|
||||
} break;
|
||||
|
||||
case KeyEvent.VK_I: {
|
||||
weight = Math.min(weight*1.01, 10);
|
||||
} break;
|
||||
|
||||
case KeyEvent.VK_U: {
|
||||
weight = Math.max(weight/1.01, 0.1);
|
||||
} break;
|
||||
|
||||
case KeyEvent.VK_O: {
|
||||
weight = 1.0;
|
||||
} break;
|
||||
|
||||
case KeyEvent.VK_F: {
|
||||
generateForest();
|
||||
} break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@ -186,6 +206,19 @@ class GruphiFrame extends JFrame {
|
||||
});
|
||||
}
|
||||
|
||||
private void generateForest() {
|
||||
if (forest != null) {
|
||||
forest = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var algo = Objects.requireNonNull(
|
||||
gruphi.getMinimumSpanningForestAlgorithm(),
|
||||
"Did you supply a spanning forest implementation");
|
||||
|
||||
forest = algo.minimumSpanningForest(graph, Comparable::compareTo, gruphi.getDirectedGraphFactory());
|
||||
}
|
||||
|
||||
private void generatePaths() {
|
||||
clearPaths();
|
||||
|
||||
@ -212,7 +245,7 @@ class GruphiFrame extends JFrame {
|
||||
if (graph.getChildrenForNode(selected).contains(n)) {
|
||||
graph.disconnectNodes(selected, n);
|
||||
} else {
|
||||
graph.connectNodes(selected, 1.0, n);
|
||||
graph.connectNodes(selected, weight, n);
|
||||
}
|
||||
} else {
|
||||
selected.pos = v;
|
||||
@ -265,6 +298,7 @@ class GruphiFrame extends JFrame {
|
||||
|
||||
private void clearGraph() {
|
||||
selected = null;
|
||||
forest = null;
|
||||
clearPaths();
|
||||
for (var node : graph.getAllNodes()) {
|
||||
graph.removeNode(node);
|
||||
@ -288,9 +322,12 @@ class GruphiFrame extends JFrame {
|
||||
|
||||
public void draw(Drawable d) {
|
||||
d.fill(Color.BLACK);
|
||||
|
||||
d.rect(0, 0, getWidth(), getHeight());
|
||||
panningAndZooming.draw(d, () ->
|
||||
drawNodes(d));
|
||||
|
||||
d.text(10, 10, String.format("Weight: %.2f", weight));
|
||||
}
|
||||
|
||||
private void drawNodes(Drawable d) {
|
||||
@ -301,6 +338,21 @@ class GruphiFrame extends JFrame {
|
||||
drawCells(d);
|
||||
|
||||
drawPath(d);
|
||||
drawForest(d);
|
||||
}
|
||||
|
||||
private void drawForest(Drawable d) {
|
||||
if (forest == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
d.strokeWeight(2);
|
||||
d.fill(Color.BLUE);
|
||||
for (var node : forest.getAllNodes()) {
|
||||
for (var child : forest.getChildrenForNode(node)) {
|
||||
d.line(node.pos.x, node.pos.y, child.pos.x, child.pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawCells(Drawable d) {
|
||||
@ -323,7 +375,6 @@ class GruphiFrame extends JFrame {
|
||||
var path = paths.get(selected);
|
||||
|
||||
d.fill(Color.GREEN);
|
||||
d.strokeWeight(2);
|
||||
Node prev = null;
|
||||
for (var node : path) {
|
||||
if (prev != null) {
|
||||
@ -336,6 +387,7 @@ class GruphiFrame extends JFrame {
|
||||
private void drawConnections(Drawable d) {
|
||||
for (var node : graph.getAllNodes()) {
|
||||
for (var child : graph.getChildrenForNode(node)) {
|
||||
d.strokeWeight(2 * graph.getArcWeightBetween(node, child));
|
||||
d.line(node.pos.x, node.pos.y, child.pos.x, child.pos.y);
|
||||
drawConnectionArrowHead(d, node, child);
|
||||
}
|
||||
@ -346,7 +398,7 @@ class GruphiFrame extends JFrame {
|
||||
var v = child.pos
|
||||
.copy()
|
||||
.sub(node.pos);
|
||||
var r = 4;
|
||||
var r = 4 * graph.getArcWeightBetween(node, child);
|
||||
var p = v.copy()
|
||||
.setMag(v.mag() - child.radius)
|
||||
.add(node.pos);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package de.oshgnacknak.gruphi;
|
||||
|
||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
||||
import h07.algorithm.ShortestPathsAlgorithm;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
@ -24,17 +25,20 @@ public class GruphiImpl implements Gruphi {
|
||||
|
||||
private final ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
||||
|
||||
private final MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm;
|
||||
|
||||
public GruphiImpl(Long frameDelay,
|
||||
Double velocity,
|
||||
Double gridSpacing,
|
||||
BiPredicate<Node, Node> neighbourPredicate,
|
||||
DirectedGraphFactory<Node, Double> directedGraphFactory, ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm) {
|
||||
DirectedGraphFactory<Node, Double> directedGraphFactory, ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm, MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm) {
|
||||
this.frameDelay = frameDelay == null ? DEFAULT_FRAME_DELAY : frameDelay;
|
||||
this.velocity = velocity == null ? DEFAULT_VELOCITY : velocity;
|
||||
this.gridSpacing = gridSpacing == null ? DEFAULT_GRID_SPACING : gridSpacing;
|
||||
this.neighbourPredicate = neighbourPredicate == null ? this::defaultAreNeighbours : neighbourPredicate;
|
||||
this.directedGraphFactory = Objects.requireNonNull(directedGraphFactory, "A directedGraphFactory must be set");
|
||||
this.shortestPathsAlgorithm = shortestPathsAlgorithm;
|
||||
this.minimumSpanningForestAlgorithm = minimumSpanningForestAlgorithm;
|
||||
}
|
||||
|
||||
private boolean defaultAreNeighbours(Node a, Node b) {
|
||||
@ -70,4 +74,9 @@ public class GruphiImpl implements Gruphi {
|
||||
public ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm() {
|
||||
return shortestPathsAlgorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinimumSpanningForestAlgorithm<Node, Double> getMinimumSpanningForestAlgorithm() {
|
||||
return minimumSpanningForestAlgorithm;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
package de.oshgnacknak.gruphi;
|
||||
|
||||
import h07.algorithm.Dijkstra;
|
||||
import h07.algorithm.Kruskal;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
public class GruphiMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
var gruphi = new GruphiBuilder()
|
||||
// .setDirectedGraphFactory(someFactory)
|
||||
// .setShortestPathsAlgorithm(somePathFinder)
|
||||
.setDirectedGraphFactory(DirectedGraphFactory.defaultFactory())
|
||||
.setShortestPathsAlgorithm(new Dijkstra<>())
|
||||
.setMinimumSpanningForestAlgorithm(new Kruskal<>())
|
||||
.createGruphi();
|
||||
|
||||
var frame = new GruphiFrame(gruphi);
|
||||
|
@ -40,8 +40,8 @@ public class MazeGenerator<V> {
|
||||
stack.push(current);
|
||||
var neighbour = unvisited.get(random.nextInt(unvisited.size()));
|
||||
|
||||
graph.connectNodes(current, random.nextDouble(), neighbour);
|
||||
graph.connectNodes(neighbour, random.nextDouble(), current);
|
||||
graph.connectNodes(current, 1.0, neighbour);
|
||||
graph.connectNodes(neighbour, 1.0, current);
|
||||
|
||||
visited.add(neighbour);
|
||||
stack.push(neighbour);
|
||||
|
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));
|
||||
}
|
||||
}
|
||||
}
|
64
src/main/java/h07/algorithm/Kruskal.java
Normal file
64
src/main/java/h07/algorithm/Kruskal.java
Normal file
@ -0,0 +1,64 @@
|
||||
package h07.algorithm;
|
||||
|
||||
import h07.graph.DirectedGraph;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Kruskal<V, A> implements MinimumSpanningForestAlgorithm<V, A> {
|
||||
|
||||
@Override
|
||||
public DirectedGraph<V, A> minimumSpanningForest(DirectedGraph<V, A> graph, Comparator<? super A> comparator, DirectedGraphFactory<V, A> factory) {
|
||||
var output = factory.createDirectedGraph();
|
||||
var sets = new ArrayList<Set<V>>();
|
||||
|
||||
for (V node : graph.getAllNodes()) {
|
||||
output.addNode(node);
|
||||
|
||||
var set = new HashSet<V>();
|
||||
set.add(node);
|
||||
sets.add(set);
|
||||
}
|
||||
|
||||
var egdes = graph.getAllNodes()
|
||||
.stream()
|
||||
.flatMap(n ->
|
||||
graph.getChildrenForNode(n)
|
||||
.stream()
|
||||
.map(c ->
|
||||
new Pair<>(n, c)))
|
||||
.sorted(Comparator.comparing(
|
||||
p -> graph.getArcWeightBetween(p.fst, p.snd),
|
||||
comparator));
|
||||
|
||||
egdes.forEach(p -> {
|
||||
var s1 = sets
|
||||
.stream()
|
||||
.filter(s -> s.contains(p.fst))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
if (s1.contains(p.snd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var s2 = sets
|
||||
.stream()
|
||||
.filter(s -> s.contains(p.snd))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
s1.addAll(s2);
|
||||
sets.remove(s2);
|
||||
|
||||
var arc = graph.getArcWeightBetween(p.fst, p.snd);
|
||||
output.connectNodes(p.fst, arc, p.snd);
|
||||
output.connectNodes(p.snd, arc, p.fst);
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package h07.algorithm;
|
||||
|
||||
import h07.graph.DirectedGraph;
|
||||
import h07.graph.DirectedGraphFactory;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public interface MinimumSpanningForestAlgorithm<V, A> {
|
||||
|
||||
DirectedGraph<V, A> minimumSpanningForest(DirectedGraph<V, A> graph, Comparator<? super A> comparator, DirectedGraphFactory<V, A> factory);
|
||||
}
|
11
src/main/java/h07/algorithm/Pair.java
Normal file
11
src/main/java/h07/algorithm/Pair.java
Normal file
@ -0,0 +1,11 @@
|
||||
package h07.algorithm;
|
||||
|
||||
public class Pair<T, S> {
|
||||
public T fst;
|
||||
public S snd;
|
||||
|
||||
public Pair(T fst, S snd) {
|
||||
this.fst = fst;
|
||||
this.snd = snd;
|
||||
}
|
||||
}
|
@ -13,4 +13,8 @@ public interface DirectedGraphFactory<V, A> {
|
||||
* @return der {@code DirectedGraph}, der von dieses Fabrik erzeugt wird.
|
||||
*/
|
||||
DirectedGraph<V, A> createDirectedGraph();
|
||||
|
||||
static <V, A> DirectedGraphFactory<V, A> defaultFactory() {
|
||||
return DirectedGraphImpl::new;
|
||||
}
|
||||
}
|
||||
|
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