WIP 3.1.3

This commit is contained in:
joachimschmidt557 2019-02-19 16:34:06 +01:00
parent 6102da73fa
commit f4eec30656
2 changed files with 182 additions and 121 deletions

View file

@ -5,144 +5,203 @@ import java.util.*;
/**
* Abstrakte generische Klasse um Wege zwischen Knoten in einem Graph zu finden.
* Eine implementierende Klasse ist beispielsweise {@link game.map.PathFinding}
*
* @param <T> Die Datenstruktur des Graphen
*/
public abstract class GraphAlgorithm<T> {
/**
* Innere Klasse um {@link Node} zu erweitern, aber nicht zu verändern
* Sie weist jedem Knoten einen Wert und einen Vorgängerknoten zu.
* @param <T>
*/
private static class AlgorithmNode<T> {
/**
* Innere Klasse um {@link Node} zu erweitern, aber nicht zu verändern Sie weist
* jedem Knoten einen Wert und einen Vorgängerknoten zu.
*
* @param <T>
*/
private static class AlgorithmNode<T> {
private Node<T> node;
private double value;
private AlgorithmNode<T> previous;
private Node<T> node;
private double value;
private AlgorithmNode<T> previous;
AlgorithmNode(Node<T> parentNode, AlgorithmNode<T> previousNode, double value) {
this.node = parentNode;
this.previous = previousNode;
this.value = value;
}
}
AlgorithmNode(Node<T> parentNode, AlgorithmNode<T> previousNode, double value) {
this.node = parentNode;
this.previous = previousNode;
this.value = value;
}
}
private Graph<T> graph;
private Graph<T> graph;
// Diese Liste enthält alle Knoten, die noch nicht abgearbeitet wurden
private List<Node<T>> availableNodes;
// Diese Liste enthält alle Knoten, die noch nicht abgearbeitet wurden
private List<Node<T>> availableNodes;
// Diese Map enthält alle Zuordnungen
private Map<Node<T>, AlgorithmNode<T>> algorithmNodes;
// Diese Map enthält alle Zuordnungen
private Map<Node<T>, AlgorithmNode<T>> algorithmNodes;
/**
* Erzeugt ein neues GraphAlgorithm-Objekt mit dem dazugehörigen Graphen und dem Startknoten.
* @param graph der zu betrachtende Graph
* @param sourceNode der Startknoten
*/
public GraphAlgorithm(Graph<T> graph, Node<T> sourceNode) {
this.graph = graph;
this.availableNodes = new LinkedList<>(graph.getNodes());
this.algorithmNodes = new HashMap<>();
/**
* Erzeugt ein neues GraphAlgorithm-Objekt mit dem dazugehörigen Graphen und dem
* Startknoten.
*
* @param graph der zu betrachtende Graph
* @param sourceNode der Startknoten
*/
public GraphAlgorithm(Graph<T> graph, Node<T> sourceNode) {
this.graph = graph;
this.availableNodes = new LinkedList<>(graph.getNodes());
this.algorithmNodes = new HashMap<>();
for(Node<T> node : graph.getNodes())
this.algorithmNodes.put(node, new AlgorithmNode<>(node, null, -1));
for (Node<T> node : graph.getNodes())
this.algorithmNodes.put(node, new AlgorithmNode<>(node, null, -1));
this.algorithmNodes.get(sourceNode).value = 0;
}
this.algorithmNodes.get(sourceNode).value = 0;
}
/**
* Diese Methode gibt einen Knoten mit dem kleinsten Wert, der noch nicht abgearbeitet wurde, zurück und entfernt ihn aus der Liste {@link #availableNodes}.
* Sollte kein Knoten gefunden werden, wird null zurückgegeben.
* Verbindliche Anforderung: Verwenden Sie beim Durchlaufen der Liste Iteratoren
* @return Der nächste abzuarbeitende Knoten oder null
*/
private AlgorithmNode<T> getSmallestNode() {
// TODO: GraphAlgorithm<T>#getSmallestNode()
if(availableNodes.isEmpty())
{
return null;
}
Iterator<Node<T>> iter = availableNodes.iterator();
Iterator<Node<T>> iterMinElem = iter;
AlgorithmNode<T> MinElem;
AlgorithmNode<T> tempElem;
if(iter.hasNext())
{
MinElem = algorithmNodes.get(iter.next());
while (iter.hasNext())
{
tempElem = algorithmNodes.get(iter.next());
if(tempElem.value < MinElem.value)
{
iterMinElem = iter;
MinElem = tempElem;
}
}
iter.remove();
return MinElem;
}
return null;
}
/**
* Diese Methode gibt einen Knoten mit dem kleinsten Wert, der noch nicht
* abgearbeitet wurde, zurück und entfernt ihn aus der Liste
* {@link #availableNodes}. Sollte kein Knoten gefunden werden, wird null
* zurückgegeben. Verbindliche Anforderung: Verwenden Sie beim Durchlaufen der
* Liste Iteratoren
*
* @return Der nächste abzuarbeitende Knoten oder null
*/
private AlgorithmNode<T> getSmallestNode() {
/**
* Diese Methode startet den Algorithmus. Dieser funktioniert wie folgt:
* 1. Suche den Knoten mit dem geringsten Wert (siehe {@link #getSmallestNode()})
* 2. Für jede angrenzende Kante:
* 2a. Überprüfe ob die Kante passierbar ist ({@link #isPassable(Edge)})
* 2b. Berechne den Wert des Knotens, in dem du den aktuellen Wert des Knotens und den der Kante addierst
* 2c. Ist der alte Wert nicht gesetzt (-1) oder ist der neue Wert kleiner, setze den neuen Wert und den Vorgängerknoten
* 3. Wiederhole solange, bis alle Knoten abgearbeitet wurden
if (availableNodes.isEmpty()) {
return null;
}
Iterator<Node<T>> iter = availableNodes.iterator();
Iterator<Node<T>> iterMinElem = iter;
AlgorithmNode<T> MinElem;
AlgorithmNode<T> tempElem;
* Nützliche Methoden:
* @see #getSmallestNode()
* @see #isPassable(Edge)
* @see Graph#getEdges(Node)
* @see Edge#getOtherNode(Node)
*/
public void run() {
// TODO: GraphAlgorithm<T>#run()
}
if (iter.hasNext()) {
MinElem = algorithmNodes.get(iter.next());
while (iter.hasNext()) {
tempElem = algorithmNodes.get(iter.next());
if (tempElem.value < MinElem.value) {
iterMinElem = iter;
MinElem = tempElem;
}
}
iter.remove();
return MinElem;
}
/**
* Diese Methode gibt eine Liste von Kanten zurück, die einen Pfad zu dem angegebenen Zielknoten representiert.
* Dabei werden zuerst beginnend mit dem Zielknoten alle Kanten mithilfe des Vorgängerattributs {@link AlgorithmNode#previous} zu der Liste hinzugefügt.
* Zum Schluss muss die Liste nur noch umgedreht werden. Sollte kein Pfad existieren, geben Sie null zurück.
* @param destination Der Zielknoten des Pfads
* @return eine Liste von Kanten oder null
*/
public List<Edge<T>> getPath(Node<T> destination) {
// TODO: GraphAlgorithm<T>#getPath(Node<T>)
return null;
}
return null;
}
/**
* Gibt den betrachteten Graphen zurück
* @return der zu betrachtende Graph
*/
protected Graph<T> getGraph() {
return this.graph;
}
/**
* Diese Methode startet den Algorithmus. Dieser funktioniert wie folgt: 1.
* Suche den Knoten mit dem geringsten Wert (siehe {@link #getSmallestNode()})
* 2. Für jede angrenzende Kante: 2a. Überprüfe ob die Kante passierbar ist
* ({@link #isPassable(Edge)}) 2b. Berechne den Wert des Knotens, in dem du den
* aktuellen Wert des Knotens und den der Kante addierst 2c. Ist der alte Wert
* nicht gesetzt (-1) oder ist der neue Wert kleiner, setze den neuen Wert und
* den Vorgängerknoten 3. Wiederhole solange, bis alle Knoten abgearbeitet
* wurden
*
* Nützliche Methoden:
*
* @see #getSmallestNode()
* @see #isPassable(Edge)
* @see Graph#getEdges(Node)
* @see Edge#getOtherNode(Node)
*/
public void run() {
/**
* Gibt den Wert einer Kante zurück.
* Diese Methode ist abstrakt und wird in den implementierenden Klassen definiert um eigene Kriterien für Werte zu ermöglichen.
* @param edge Eine Kante
* @return Ein Wert, der der Kante zugewiesen wird
*/
protected abstract double getValue(Edge<T> edge);
AlgorithmNode<T> v = getSmallestNode();
while (v != null) {
for (Edge<T> e : graph.getEdges(v.node)) {
if (isPassable(e)) {
AlgorithmNode<T> n = algorithmNodes.get(e.getOtherNode(v.node));
double a = v.value + getValue(e);
if (n.value == -1 || n.value > a) {
n.value = a;
n.previous = v;
}
}
}
v = getSmallestNode();
}
}
/**
* Gibt an, ob eine Kante passierbar ist.
* @param edge Eine Kante
* @return true, wenn die Kante passierbar ist.
*/
protected abstract boolean isPassable(Edge<T> edge);
/**
* Diese Methode gibt eine Liste von Kanten zurück, die einen Pfad zu dem
* angegebenen Zielknoten representiert. Dabei werden zuerst beginnend mit dem
* Zielknoten alle Kanten mithilfe des Vorgängerattributs
* {@link AlgorithmNode#previous} zu der Liste hinzugefügt. Zum Schluss muss die
* Liste nur noch umgedreht werden. Sollte kein Pfad existieren, geben Sie null
* zurück.
*
* @param destination Der Zielknoten des Pfads
* @return eine Liste von Kanten oder null
*/
public List<Edge<T>> getPath(Node<T> destination) {
/**
* Gibt an, ob eine Knoten passierbar ist.
* @param node Eine Knoten
* @return true, wenn der Knoten passierbar ist.
*/
protected abstract boolean isPassable(Node<T> node);
// Stage 1
ArrayList<Edge<T>> reversePath = new ArrayList<Edge<T>>();
AlgorithmNode<T> prevNode = algorithmNodes.get(destination);
if (prevNode == null)
return null;
while (!prevNode.node.equals(destination)) {
AlgorithmNode<T> prevPrevNode = prevNode.previous;
if (prevPrevNode == null)
return null;
reversePath.add(new Edge<T>(prevPrevNode.node, prevNode.node));
prevNode = prevPrevNode;
}
// Stage 2
Collections.reverse(reversePath);
return reversePath;
}
/**
* Gibt den betrachteten Graphen zurück
*
* @return der zu betrachtende Graph
*/
protected Graph<T> getGraph() {
return this.graph;
}
/**
* Gibt den Wert einer Kante zurück. Diese Methode ist abstrakt und wird in den
* implementierenden Klassen definiert um eigene Kriterien für Werte zu
* ermöglichen.
*
* @param edge Eine Kante
* @return Ein Wert, der der Kante zugewiesen wird
*/
protected abstract double getValue(Edge<T> edge);
/**
* Gibt an, ob eine Kante passierbar ist.
*
* @param edge Eine Kante
* @return true, wenn die Kante passierbar ist.
*/
protected abstract boolean isPassable(Edge<T> edge);
/**
* Gibt an, ob ein Knoten passierbar ist.
*
* @param node Ein Knoten
* @return true, wenn der Knoten passierbar ist.
*/
protected abstract boolean isPassable(Node<T> node);
}

View file

@ -90,7 +90,9 @@
\begin{itemize}
\item HashSet wird verwendet, um die bisher
besuchten Knoten zu speichern.
besuchten Knoten zu speichern. Eine HashSet
hat den Vorteil, dass Elemente nur einmal
gespeichert werden können.
\item ArrayDeque wird verwendet, um die nächsten
Knoten, die besucht werden, zu speichern.
\end{itemize}