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. * Abstrakte generische Klasse um Wege zwischen Knoten in einem Graph zu finden.
* Eine implementierende Klasse ist beispielsweise {@link game.map.PathFinding} * Eine implementierende Klasse ist beispielsweise {@link game.map.PathFinding}
*
* @param <T> Die Datenstruktur des Graphen * @param <T> Die Datenstruktur des Graphen
*/ */
public abstract class GraphAlgorithm<T> { public abstract class GraphAlgorithm<T> {
/** /**
* Innere Klasse um {@link Node} zu erweitern, aber nicht zu verändern * Innere Klasse um {@link Node} zu erweitern, aber nicht zu verändern Sie weist
* Sie weist jedem Knoten einen Wert und einen Vorgängerknoten zu. * jedem Knoten einen Wert und einen Vorgängerknoten zu.
* @param <T> *
*/ * @param <T>
private static class AlgorithmNode<T> { */
private static class AlgorithmNode<T> {
private Node<T> node; private Node<T> node;
private double value; private double value;
private AlgorithmNode<T> previous; private AlgorithmNode<T> previous;
AlgorithmNode(Node<T> parentNode, AlgorithmNode<T> previousNode, double value) { AlgorithmNode(Node<T> parentNode, AlgorithmNode<T> previousNode, double value) {
this.node = parentNode; this.node = parentNode;
this.previous = previousNode; this.previous = previousNode;
this.value = value; this.value = value;
} }
} }
private Graph<T> graph; private Graph<T> graph;
// Diese Liste enthält alle Knoten, die noch nicht abgearbeitet wurden // Diese Liste enthält alle Knoten, die noch nicht abgearbeitet wurden
private List<Node<T>> availableNodes; private List<Node<T>> availableNodes;
// Diese Map enthält alle Zuordnungen // Diese Map enthält alle Zuordnungen
private Map<Node<T>, AlgorithmNode<T>> algorithmNodes; private Map<Node<T>, AlgorithmNode<T>> algorithmNodes;
/** /**
* Erzeugt ein neues GraphAlgorithm-Objekt mit dem dazugehörigen Graphen und dem Startknoten. * Erzeugt ein neues GraphAlgorithm-Objekt mit dem dazugehörigen Graphen und dem
* @param graph der zu betrachtende Graph * Startknoten.
* @param sourceNode der Startknoten *
*/ * @param graph der zu betrachtende Graph
public GraphAlgorithm(Graph<T> graph, Node<T> sourceNode) { * @param sourceNode der Startknoten
this.graph = graph; */
this.availableNodes = new LinkedList<>(graph.getNodes()); public GraphAlgorithm(Graph<T> graph, Node<T> sourceNode) {
this.algorithmNodes = new HashMap<>(); this.graph = graph;
this.availableNodes = new LinkedList<>(graph.getNodes());
this.algorithmNodes = new HashMap<>();
for(Node<T> node : graph.getNodes()) for (Node<T> node : graph.getNodes())
this.algorithmNodes.put(node, new AlgorithmNode<>(node, null, -1)); 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}. * Diese Methode gibt einen Knoten mit dem kleinsten Wert, der noch nicht
* Sollte kein Knoten gefunden werden, wird null zurückgegeben. * abgearbeitet wurde, zurück und entfernt ihn aus der Liste
* Verbindliche Anforderung: Verwenden Sie beim Durchlaufen der Liste Iteratoren * {@link #availableNodes}. Sollte kein Knoten gefunden werden, wird null
* @return Der nächste abzuarbeitende Knoten oder null * zurückgegeben. Verbindliche Anforderung: Verwenden Sie beim Durchlaufen der
*/ * Liste Iteratoren
private AlgorithmNode<T> getSmallestNode() { *
// TODO: GraphAlgorithm<T>#getSmallestNode() * @return Der nächste abzuarbeitende Knoten oder null
if(availableNodes.isEmpty()) */
{ private AlgorithmNode<T> getSmallestNode() {
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;
}
/** if (availableNodes.isEmpty()) {
* Diese Methode startet den Algorithmus. Dieser funktioniert wie folgt: return null;
* 1. Suche den Knoten mit dem geringsten Wert (siehe {@link #getSmallestNode()}) }
* 2. Für jede angrenzende Kante: Iterator<Node<T>> iter = availableNodes.iterator();
* 2a. Überprüfe ob die Kante passierbar ist ({@link #isPassable(Edge)}) Iterator<Node<T>> iterMinElem = iter;
* 2b. Berechne den Wert des Knotens, in dem du den aktuellen Wert des Knotens und den der Kante addierst AlgorithmNode<T> MinElem;
* 2c. Ist der alte Wert nicht gesetzt (-1) oder ist der neue Wert kleiner, setze den neuen Wert und den Vorgängerknoten AlgorithmNode<T> tempElem;
* 3. Wiederhole solange, bis alle Knoten abgearbeitet wurden
* Nützliche Methoden: if (iter.hasNext()) {
* @see #getSmallestNode() MinElem = algorithmNodes.get(iter.next());
* @see #isPassable(Edge) while (iter.hasNext()) {
* @see Graph#getEdges(Node) tempElem = algorithmNodes.get(iter.next());
* @see Edge#getOtherNode(Node) if (tempElem.value < MinElem.value) {
*/ iterMinElem = iter;
public void run() { MinElem = tempElem;
// TODO: GraphAlgorithm<T>#run() }
} }
iter.remove();
return MinElem;
}
/** return null;
* 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;
}
/** /**
* Gibt den betrachteten Graphen zurück * Diese Methode startet den Algorithmus. Dieser funktioniert wie folgt: 1.
* @return der zu betrachtende Graph * Suche den Knoten mit dem geringsten Wert (siehe {@link #getSmallestNode()})
*/ * 2. Für jede angrenzende Kante: 2a. Überprüfe ob die Kante passierbar ist
protected Graph<T> getGraph() { * ({@link #isPassable(Edge)}) 2b. Berechne den Wert des Knotens, in dem du den
return this.graph; * 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() {
/** AlgorithmNode<T> v = getSmallestNode();
* 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);
/** while (v != null) {
* Gibt an, ob eine Kante passierbar ist. for (Edge<T> e : graph.getEdges(v.node)) {
* @param edge Eine Kante if (isPassable(e)) {
* @return true, wenn die Kante passierbar ist.
*/
protected abstract boolean isPassable(Edge<T> edge);
/** AlgorithmNode<T> n = algorithmNodes.get(e.getOtherNode(v.node));
* Gibt an, ob eine Knoten passierbar ist.
* @param node Eine Knoten double a = v.value + getValue(e);
* @return true, wenn der Knoten passierbar ist.
*/ if (n.value == -1 || n.value > a) {
protected abstract boolean isPassable(Node<T> node);
n.value = a;
n.previous = v;
}
}
}
v = getSmallestNode();
}
}
/**
* 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) {
// 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} \begin{itemize}
\item HashSet wird verwendet, um die bisher \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 \item ArrayDeque wird verwendet, um die nächsten
Knoten, die besucht werden, zu speichern. Knoten, die besucht werden, zu speichern.
\end{itemize} \end{itemize}