Compare commits
No commits in common. "master" and "7e44d0fb3fd7bf76987acb98ea96b934bd6673cb" have entirely different histories.
master
...
7e44d0fb3f
18 changed files with 14 additions and 511 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
src/main/java/h07
|
||||||
|
|
||||||
### VS-Code ###
|
### VS-Code ###
|
||||||
.vscode/
|
.vscode/
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
|
@ -45,15 +45,6 @@ Startknoten (grün) auswählen löscht Pfäde.
|
||||||
- Escape, `Q`:
|
- Escape, `Q`:
|
||||||
Beenden
|
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)
|
## Wenn Knoten ausgewählt (rot)
|
||||||
|
|
||||||
- WASD:
|
- WASD:
|
||||||
|
|
|
@ -75,19 +75,6 @@ public class Canvas extends JPanel {
|
||||||
r.run();
|
r.run();
|
||||||
g.scale(1/scale, 1/scale);
|
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,8 +23,4 @@ public interface Drawable {
|
||||||
void translated(double x, double y, Runnable r);
|
void translated(double x, double y, Runnable r);
|
||||||
|
|
||||||
void scaled(double scale, Runnable r);
|
void scaled(double scale, Runnable r);
|
||||||
|
|
||||||
void text(double x, double y, String text);
|
|
||||||
|
|
||||||
void textSize(int size);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package de.oshgnacknak.gruphi;
|
package de.oshgnacknak.gruphi;
|
||||||
|
|
||||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
|
||||||
import h07.algorithm.ShortestPathsAlgorithm;
|
import h07.algorithm.ShortestPathsAlgorithm;
|
||||||
import h07.graph.DirectedGraphFactory;
|
import h07.graph.DirectedGraphFactory;
|
||||||
|
|
||||||
|
@ -39,9 +38,4 @@ public interface Gruphi {
|
||||||
* @return A {@link ShortestPathsAlgorithm} for the path finding
|
* @return A {@link ShortestPathsAlgorithm} for the path finding
|
||||||
*/
|
*/
|
||||||
ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm();
|
ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return A {@link MinimumSpanningForestAlgorithm} for the spanning tree calculation
|
|
||||||
*/
|
|
||||||
MinimumSpanningForestAlgorithm<Node, Double> getMinimumSpanningForestAlgorithm();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package de.oshgnacknak.gruphi;
|
package de.oshgnacknak.gruphi;
|
||||||
|
|
||||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
|
||||||
import h07.algorithm.ShortestPathsAlgorithm;
|
import h07.algorithm.ShortestPathsAlgorithm;
|
||||||
import h07.graph.DirectedGraphFactory;
|
import h07.graph.DirectedGraphFactory;
|
||||||
|
|
||||||
|
@ -13,7 +12,6 @@ public class GruphiBuilder {
|
||||||
private BiPredicate<Node, Node> neighbourPredicate;
|
private BiPredicate<Node, Node> neighbourPredicate;
|
||||||
private DirectedGraphFactory<Node, Double> directedGraphFactory;
|
private DirectedGraphFactory<Node, Double> directedGraphFactory;
|
||||||
private ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
private ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
||||||
private MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm;
|
|
||||||
|
|
||||||
public GruphiBuilder setFrameDelay(Long frameDelay) {
|
public GruphiBuilder setFrameDelay(Long frameDelay) {
|
||||||
this.frameDelay = frameDelay;
|
this.frameDelay = frameDelay;
|
||||||
|
@ -45,12 +43,7 @@ public class GruphiBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GruphiBuilder setMinimumSpanningForestAlgorithm(MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm) {
|
|
||||||
this.minimumSpanningForestAlgorithm = minimumSpanningForestAlgorithm;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GruphiImpl createGruphi() {
|
public GruphiImpl createGruphi() {
|
||||||
return new GruphiImpl(frameDelay, velocity, gridSpacing, neighbourPredicate, directedGraphFactory, shortestPathsAlgorithm, minimumSpanningForestAlgorithm);
|
return new GruphiImpl(frameDelay, velocity, gridSpacing, neighbourPredicate, directedGraphFactory, shortestPathsAlgorithm);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -31,8 +31,6 @@ class GruphiFrame extends JFrame {
|
||||||
|
|
||||||
private final DirectedGraph<Node, Double> graph;
|
private final DirectedGraph<Node, Double> graph;
|
||||||
|
|
||||||
private DirectedGraph<Node, Double> forest;
|
|
||||||
|
|
||||||
private final MazeGenerator<Node> mazeGenerator;
|
private final MazeGenerator<Node> mazeGenerator;
|
||||||
private final Gruphi gruphi;
|
private final Gruphi gruphi;
|
||||||
|
|
||||||
|
@ -46,7 +44,6 @@ class GruphiFrame extends JFrame {
|
||||||
private Map<Node, Path<Node, Double>> paths = null;
|
private Map<Node, Path<Node, Double>> paths = null;
|
||||||
private boolean running = true;
|
private boolean running = true;
|
||||||
private boolean nuggets = false;
|
private boolean nuggets = false;
|
||||||
private double weight = 1.0;
|
|
||||||
|
|
||||||
GruphiFrame(Gruphi gruphi) {
|
GruphiFrame(Gruphi gruphi) {
|
||||||
super("Gruphi - The Graph GUI - By Osh");
|
super("Gruphi - The Graph GUI - By Osh");
|
||||||
|
@ -114,7 +111,6 @@ class GruphiFrame extends JFrame {
|
||||||
}
|
}
|
||||||
graph.removeNode(selected);
|
graph.removeNode(selected);
|
||||||
selected = null;
|
selected = null;
|
||||||
forest = null;
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -145,22 +141,6 @@ class GruphiFrame extends JFrame {
|
||||||
generatePaths();
|
generatePaths();
|
||||||
} break;
|
} 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;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,19 +186,6 @@ 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() {
|
private void generatePaths() {
|
||||||
clearPaths();
|
clearPaths();
|
||||||
|
|
||||||
|
@ -245,7 +212,7 @@ class GruphiFrame extends JFrame {
|
||||||
if (graph.getChildrenForNode(selected).contains(n)) {
|
if (graph.getChildrenForNode(selected).contains(n)) {
|
||||||
graph.disconnectNodes(selected, n);
|
graph.disconnectNodes(selected, n);
|
||||||
} else {
|
} else {
|
||||||
graph.connectNodes(selected, weight, n);
|
graph.connectNodes(selected, 1.0, n);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
selected.pos = v;
|
selected.pos = v;
|
||||||
|
@ -298,7 +265,6 @@ class GruphiFrame extends JFrame {
|
||||||
|
|
||||||
private void clearGraph() {
|
private void clearGraph() {
|
||||||
selected = null;
|
selected = null;
|
||||||
forest = null;
|
|
||||||
clearPaths();
|
clearPaths();
|
||||||
for (var node : graph.getAllNodes()) {
|
for (var node : graph.getAllNodes()) {
|
||||||
graph.removeNode(node);
|
graph.removeNode(node);
|
||||||
|
@ -322,12 +288,9 @@ class GruphiFrame extends JFrame {
|
||||||
|
|
||||||
public void draw(Drawable d) {
|
public void draw(Drawable d) {
|
||||||
d.fill(Color.BLACK);
|
d.fill(Color.BLACK);
|
||||||
|
|
||||||
d.rect(0, 0, getWidth(), getHeight());
|
d.rect(0, 0, getWidth(), getHeight());
|
||||||
panningAndZooming.draw(d, () ->
|
panningAndZooming.draw(d, () ->
|
||||||
drawNodes(d));
|
drawNodes(d));
|
||||||
|
|
||||||
d.text(10, 10, String.format("Weight: %.2f", weight));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawNodes(Drawable d) {
|
private void drawNodes(Drawable d) {
|
||||||
|
@ -338,21 +301,6 @@ class GruphiFrame extends JFrame {
|
||||||
drawCells(d);
|
drawCells(d);
|
||||||
|
|
||||||
drawPath(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) {
|
private void drawCells(Drawable d) {
|
||||||
|
@ -375,6 +323,7 @@ class GruphiFrame extends JFrame {
|
||||||
var path = paths.get(selected);
|
var path = paths.get(selected);
|
||||||
|
|
||||||
d.fill(Color.GREEN);
|
d.fill(Color.GREEN);
|
||||||
|
d.strokeWeight(2);
|
||||||
Node prev = null;
|
Node prev = null;
|
||||||
for (var node : path) {
|
for (var node : path) {
|
||||||
if (prev != null) {
|
if (prev != null) {
|
||||||
|
@ -387,7 +336,6 @@ class GruphiFrame extends JFrame {
|
||||||
private void drawConnections(Drawable d) {
|
private void drawConnections(Drawable d) {
|
||||||
for (var node : graph.getAllNodes()) {
|
for (var node : graph.getAllNodes()) {
|
||||||
for (var child : graph.getChildrenForNode(node)) {
|
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);
|
d.line(node.pos.x, node.pos.y, child.pos.x, child.pos.y);
|
||||||
drawConnectionArrowHead(d, node, child);
|
drawConnectionArrowHead(d, node, child);
|
||||||
}
|
}
|
||||||
|
@ -398,7 +346,7 @@ class GruphiFrame extends JFrame {
|
||||||
var v = child.pos
|
var v = child.pos
|
||||||
.copy()
|
.copy()
|
||||||
.sub(node.pos);
|
.sub(node.pos);
|
||||||
var r = 4 * graph.getArcWeightBetween(node, child);
|
var r = 4;
|
||||||
var p = v.copy()
|
var p = v.copy()
|
||||||
.setMag(v.mag() - child.radius)
|
.setMag(v.mag() - child.radius)
|
||||||
.add(node.pos);
|
.add(node.pos);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package de.oshgnacknak.gruphi;
|
package de.oshgnacknak.gruphi;
|
||||||
|
|
||||||
import h07.algorithm.MinimumSpanningForestAlgorithm;
|
|
||||||
import h07.algorithm.ShortestPathsAlgorithm;
|
import h07.algorithm.ShortestPathsAlgorithm;
|
||||||
import h07.graph.DirectedGraphFactory;
|
import h07.graph.DirectedGraphFactory;
|
||||||
|
|
||||||
|
@ -25,20 +24,17 @@ public class GruphiImpl implements Gruphi {
|
||||||
|
|
||||||
private final ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
private final ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm;
|
||||||
|
|
||||||
private final MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm;
|
|
||||||
|
|
||||||
public GruphiImpl(Long frameDelay,
|
public GruphiImpl(Long frameDelay,
|
||||||
Double velocity,
|
Double velocity,
|
||||||
Double gridSpacing,
|
Double gridSpacing,
|
||||||
BiPredicate<Node, Node> neighbourPredicate,
|
BiPredicate<Node, Node> neighbourPredicate,
|
||||||
DirectedGraphFactory<Node, Double> directedGraphFactory, ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm, MinimumSpanningForestAlgorithm<Node, Double> minimumSpanningForestAlgorithm) {
|
DirectedGraphFactory<Node, Double> directedGraphFactory, ShortestPathsAlgorithm<Node, Double> shortestPathsAlgorithm) {
|
||||||
this.frameDelay = frameDelay == null ? DEFAULT_FRAME_DELAY : frameDelay;
|
this.frameDelay = frameDelay == null ? DEFAULT_FRAME_DELAY : frameDelay;
|
||||||
this.velocity = velocity == null ? DEFAULT_VELOCITY : velocity;
|
this.velocity = velocity == null ? DEFAULT_VELOCITY : velocity;
|
||||||
this.gridSpacing = gridSpacing == null ? DEFAULT_GRID_SPACING : gridSpacing;
|
this.gridSpacing = gridSpacing == null ? DEFAULT_GRID_SPACING : gridSpacing;
|
||||||
this.neighbourPredicate = neighbourPredicate == null ? this::defaultAreNeighbours : neighbourPredicate;
|
this.neighbourPredicate = neighbourPredicate == null ? this::defaultAreNeighbours : neighbourPredicate;
|
||||||
this.directedGraphFactory = Objects.requireNonNull(directedGraphFactory, "A directedGraphFactory must be set");
|
this.directedGraphFactory = Objects.requireNonNull(directedGraphFactory, "A directedGraphFactory must be set");
|
||||||
this.shortestPathsAlgorithm = shortestPathsAlgorithm;
|
this.shortestPathsAlgorithm = shortestPathsAlgorithm;
|
||||||
this.minimumSpanningForestAlgorithm = minimumSpanningForestAlgorithm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean defaultAreNeighbours(Node a, Node b) {
|
private boolean defaultAreNeighbours(Node a, Node b) {
|
||||||
|
@ -74,9 +70,4 @@ public class GruphiImpl implements Gruphi {
|
||||||
public ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm() {
|
public ShortestPathsAlgorithm<Node, Double> getShortestPathsAlgorithm() {
|
||||||
return shortestPathsAlgorithm;
|
return shortestPathsAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MinimumSpanningForestAlgorithm<Node, Double> getMinimumSpanningForestAlgorithm() {
|
|
||||||
return minimumSpanningForestAlgorithm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
package de.oshgnacknak.gruphi;
|
package de.oshgnacknak.gruphi;
|
||||||
|
|
||||||
import h07.algorithm.Dijkstra;
|
|
||||||
import h07.algorithm.Kruskal;
|
|
||||||
import h07.graph.DirectedGraphFactory;
|
|
||||||
|
|
||||||
public class GruphiMain {
|
public class GruphiMain {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var gruphi = new GruphiBuilder()
|
var gruphi = new GruphiBuilder()
|
||||||
.setDirectedGraphFactory(DirectedGraphFactory.defaultFactory())
|
// .setDirectedGraphFactory(someFactory)
|
||||||
.setShortestPathsAlgorithm(new Dijkstra<>())
|
// .setShortestPathsAlgorithm(somePathFinder)
|
||||||
.setMinimumSpanningForestAlgorithm(new Kruskal<>())
|
|
||||||
.createGruphi();
|
.createGruphi();
|
||||||
|
|
||||||
var frame = new GruphiFrame(gruphi);
|
var frame = new GruphiFrame(gruphi);
|
||||||
|
|
|
@ -40,8 +40,8 @@ public class MazeGenerator<V> {
|
||||||
stack.push(current);
|
stack.push(current);
|
||||||
var neighbour = unvisited.get(random.nextInt(unvisited.size()));
|
var neighbour = unvisited.get(random.nextInt(unvisited.size()));
|
||||||
|
|
||||||
graph.connectNodes(current, 1.0, neighbour);
|
graph.connectNodes(current, random.nextDouble(), neighbour);
|
||||||
graph.connectNodes(neighbour, 1.0, current);
|
graph.connectNodes(neighbour, random.nextDouble(), current);
|
||||||
|
|
||||||
visited.add(neighbour);
|
visited.add(neighbour);
|
||||||
stack.push(neighbour);
|
stack.push(neighbour);
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
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,8 +13,4 @@ public interface DirectedGraphFactory<V, A> {
|
||||||
* @return der {@code DirectedGraph}, der von dieses Fabrik erzeugt wird.
|
* @return der {@code DirectedGraph}, der von dieses Fabrik erzeugt wird.
|
||||||
*/
|
*/
|
||||||
DirectedGraph<V, A> createDirectedGraph();
|
DirectedGraph<V, A> createDirectedGraph();
|
||||||
|
|
||||||
static <V, A> DirectedGraphFactory<V, A> defaultFactory() {
|
|
||||||
return DirectedGraphImpl::new;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
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,8 +95,11 @@ public interface Path<V, A> extends Iterable<V> {
|
||||||
* @throws NullPointerException falls der Knoten {@code null} ist
|
* @throws NullPointerException falls der Knoten {@code null} ist
|
||||||
*/
|
*/
|
||||||
static <V, A> Path<V, A> of(V v1) {
|
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");
|
Objects.requireNonNull(v1, "Der Knoten eines Pfades darf nicht null sein");
|
||||||
return new PathImpl<>(v1);
|
return new PathImpl<>(v1);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
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