forked from IF-LK-2020/dijkstra
Initial commit
This commit is contained in:
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
Dokumente*
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
235
Dijkstra.java
Normal file
235
Dijkstra.java
Normal file
@@ -0,0 +1,235 @@
|
||||
public class Dijkstra {
|
||||
|
||||
private Graph graph;
|
||||
|
||||
private boolean verbose;
|
||||
|
||||
/**
|
||||
* Implementierung des Dijkstra-Algorithmus auf ungerichteten,
|
||||
* gewichteten Grapen.
|
||||
*/
|
||||
public Dijkstra() {
|
||||
graph = new Graph();
|
||||
verbose = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schaltet die ausführliche Ausgabe ein bzw. aus.
|
||||
*/
|
||||
public void toggleVerbose() {
|
||||
verbose = !verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen Testgraphen und führt den Algorithmus für die Strecke
|
||||
* Münster -> Lünen aus.
|
||||
*/
|
||||
public void test() {
|
||||
System.out.println("Creating graph..");
|
||||
createGraphExample1();
|
||||
System.out.println("Searching for shortest path..");
|
||||
List<Vertex> path = findShortestPath("Münster", "Lünen");
|
||||
//List<Vertex> path = findShortestPath("Haltern", "Münster");
|
||||
System.out.println();
|
||||
System.out.println("Result:");
|
||||
printPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt den Dijkstra-Algorithmus von einem Startknoten aus und bricht ab,
|
||||
* sobald der kürzeste Weg zum Zielknoten gefunden wurde.
|
||||
*
|
||||
* Der kürzeste Weg wird als Liste von Knoten zurückgegeben. Der kürzeste
|
||||
* Weg zu den Knoten (auf dem Weg) wird im Attribut {@link Vertex#getValue() value}
|
||||
* der Knoten gespeichert.
|
||||
*
|
||||
* @param from Startknoten
|
||||
* @param to Endknoten
|
||||
*/
|
||||
public List<Vertex> findShortestPath( String from, String to ) {
|
||||
Vertex start = graph.getVertex(from);
|
||||
Vertex end = graph.getVertex(to);
|
||||
|
||||
// Markierungen zeigen an, ob ein Knoten schon in die
|
||||
// Liste einsortiert wurde
|
||||
graph.setAllEdgeMarks(false);
|
||||
|
||||
List<Vertex> vList = new List<>();
|
||||
// Startknoten kommt als Erster in der Liste,
|
||||
// mit Weglänge 0.0
|
||||
start.setValue(0.0);
|
||||
start.setMark(true);
|
||||
vList.append(start);
|
||||
|
||||
while( !vList.isEmpty() ) {
|
||||
Vertex current = findMinVertex(vList);
|
||||
if( verbose )
|
||||
System.out.println("Current node: " + current.getID());
|
||||
if( current.equals(end) ) {
|
||||
// Kürzester Weg für den Zielknoten gefunden
|
||||
// Können abbrechen
|
||||
if( verbose )
|
||||
System.out.println(" Vertex found; breaking loop");
|
||||
break;
|
||||
}
|
||||
|
||||
List<Vertex> neighbours = graph.getNeighbours(current);
|
||||
neighbours.toFirst();
|
||||
while( neighbours.hasAccess() ) {
|
||||
Vertex v = neighbours.getContent();
|
||||
Edge e = graph.getEdge(current, v);
|
||||
|
||||
if( verbose )
|
||||
System.out.println(" Checking neighbour: " + v.getID());
|
||||
|
||||
if( current.getValue() + e.getWeight() < v.getValue() ) {
|
||||
// Es wurde ein kürzerer Weg gefunden
|
||||
// Der Knoten wird aktualisiert
|
||||
if( verbose ) {
|
||||
if (v.getValue() == Integer.MAX_VALUE) {
|
||||
System.out.printf(" Shorter path found: %.2f < Infinity\n", current.getValue() + e.getWeight(), v.getValue());
|
||||
} else {
|
||||
System.out.printf(" Shorter path found: %.2f < %.2f\n", current.getValue() + e.getWeight(), v.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
v.setValue(current.getValue() + e.getWeight());
|
||||
v.setPredecessor(current);
|
||||
} else {
|
||||
// Kein kürzerer Weg gefunden
|
||||
if( verbose )
|
||||
System.out.printf(" No update: %.2f <= %.2f\n", v.getValue(), current.getValue() + e.getWeight());
|
||||
}
|
||||
if( !v.isMarked() ) {
|
||||
// Der Knoten muss noch in die Liste einsortiert werden.
|
||||
v.setMark(true);
|
||||
vList.append(v);
|
||||
if( verbose )
|
||||
System.out.println(" Added node to list.");
|
||||
}
|
||||
|
||||
neighbours.next();
|
||||
}
|
||||
}
|
||||
|
||||
return generatePath(end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht den Vertex mit dem geringsten Wert aus der Liste, entfernt ihn
|
||||
* und gibt den Vertex zurück.
|
||||
* @param pList
|
||||
* @return
|
||||
*/
|
||||
private Vertex findMinVertex( List<Vertex> pList ) {
|
||||
// Knoten mit kleinstem Wert suchen
|
||||
pList.toFirst();
|
||||
Vertex min = pList.getContent();
|
||||
pList.next();
|
||||
while( pList.hasAccess() ) {
|
||||
if (pList.getContent().getValue() < min.getValue() ) {
|
||||
min = pList.getContent();
|
||||
}
|
||||
pList.next();
|
||||
}
|
||||
// Knoten wieder suchen und entfernen
|
||||
pList.toFirst();
|
||||
while( !pList.getContent().equals(min) ) {
|
||||
pList.next();
|
||||
}
|
||||
pList.remove();
|
||||
// Fertig
|
||||
return min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt <strong>nach</strong> Ablauf des Algorithmus den Weg zu einem
|
||||
* Knoten aus den {@link Vertex#getPredecessor() predecessor} Attributen
|
||||
* der Knoten.
|
||||
*
|
||||
* @param pVertex
|
||||
* @return
|
||||
*/
|
||||
public List<Vertex> generatePath( Vertex pVertex ) {
|
||||
List<Vertex> shortestpath = new List<>();
|
||||
double length = 0.0;
|
||||
Vertex current = pVertex;
|
||||
while( current != null ) {
|
||||
shortestpath.insert(current);
|
||||
shortestpath.toFirst();
|
||||
current = current.getPredecessor();
|
||||
}
|
||||
|
||||
return shortestpath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt einen Pfad aus, der in Form einer Liste von Knoten vorliegt. Es
|
||||
* wird davon ausgegangen, dass die Knoten einen zusammenhängenden Pfad
|
||||
* darstellen. Ob jeweils Kanten zwischen den Folgeknoten vorliegen wird
|
||||
* nicht geprüft.
|
||||
* @param pPath
|
||||
*/
|
||||
public void printPath( List<Vertex> pPath ) {
|
||||
double length = 0.0;
|
||||
|
||||
pPath.toFirst();
|
||||
while(pPath.hasAccess()) {
|
||||
System.out.println("- " + pPath.getContent().getID());
|
||||
length = pPath.getContent().getValue();
|
||||
pPath.next();
|
||||
}
|
||||
|
||||
System.out.printf("Length of shortest path: %.2f\n", length);
|
||||
}
|
||||
|
||||
public void createGraphExample1() {
|
||||
graph = new Graph();
|
||||
|
||||
String[] ids = new String[]{
|
||||
"Münster", "Hamm", "Dülmen",
|
||||
"Haltern", "Lüdinghsn", "Werne", "Lünen", "Kamen",
|
||||
"y", "x", "3", "79", "80"
|
||||
};
|
||||
Vertex[] v = new Vertex[ids.length];
|
||||
for( int i = 0; i < ids.length; i++ ) {
|
||||
v[i] = new Vertex(ids[i]);
|
||||
graph.addVertex(v[i]);
|
||||
}
|
||||
|
||||
graph.addEdge(new Edge(v[0], v[1], 40.0));
|
||||
graph.addEdge(new Edge(v[0], v[11], 24.0));
|
||||
graph.addEdge(new Edge(v[0], v[5], 37.0));
|
||||
graph.addEdge(new Edge(v[0], v[9], 8.0));
|
||||
graph.addEdge(new Edge(v[0], v[8], 13.0));
|
||||
|
||||
graph.addEdge(new Edge(v[1], v[7], 17.0));
|
||||
graph.addEdge(new Edge(v[1], v[6], 24.0));
|
||||
graph.addEdge(new Edge(v[1], v[11], 24.0));
|
||||
|
||||
graph.addEdge(new Edge(v[2], v[8], 31.0));
|
||||
graph.addEdge(new Edge(v[2], v[3], 12.0));
|
||||
|
||||
graph.addEdge(new Edge(v[3], v[4], 20.0));
|
||||
|
||||
graph.addEdge(new Edge(v[4], v[10], 16.0));
|
||||
graph.addEdge(new Edge(v[4], v[11], 15.0));
|
||||
graph.addEdge(new Edge(v[4], v[6], 21.0));
|
||||
|
||||
graph.addEdge(new Edge(v[5], v[12], 5.0));
|
||||
graph.addEdge(new Edge(v[5], v[6], 10.0));
|
||||
|
||||
graph.addEdge(new Edge(v[6], v[8], 12.0));
|
||||
|
||||
graph.addEdge(new Edge(v[8], v[10], 1.0));
|
||||
|
||||
graph.addEdge(new Edge(v[9], v[10], 4.0));
|
||||
graph.addEdge(new Edge(v[9], v[11], 15.0));
|
||||
|
||||
graph.addEdge(new Edge(v[11], v[12], 11.0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
75
Edge.java
Normal file
75
Edge.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* <p>
|
||||
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
|
||||
* </p>
|
||||
* <p>
|
||||
* Klasse Edge
|
||||
* </p>
|
||||
* <p>
|
||||
* Die Klasse Edge stellt eine einzelne, ungerichtete Kante eines Graphen dar.
|
||||
* Beim Erstellen werden die beiden durch sie zu verbindenden Knotenobjekte und eine
|
||||
* Gewichtung als double uebergeben. Beide Knotenobjekte koennen abgefragt werden.
|
||||
* Des Weiteren koennen die Gewichtung und eine Markierung gesetzt und abgefragt werden.
|
||||
* </p>
|
||||
*
|
||||
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
|
||||
* @version Oktober 2015
|
||||
*/
|
||||
public class Edge{
|
||||
private Vertex[] vertices;
|
||||
private double weight;
|
||||
private boolean mark;
|
||||
|
||||
/**
|
||||
* Ein neues Objekt vom Typ Edge wird erstellt. Die von diesem Objekt
|
||||
* repraesentierte Kante verbindet die Knoten pVertex und pAnotherVertex mit der
|
||||
* Gewichtung pWeight. Ihre Markierung hat den Wert false.
|
||||
*/
|
||||
public Edge(Vertex pVertex, Vertex pAnotherVertex, double pWeight){
|
||||
vertices = new Vertex[2];
|
||||
vertices[0] = pVertex;
|
||||
vertices[1] = pAnotherVertex;
|
||||
weight = pWeight;
|
||||
mark = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage gibt die beiden Knoten, die durch die Kante verbunden werden, als neues Feld vom Typ Vertex zurueck. Das Feld hat
|
||||
* genau zwei Eintraege mit den Indexwerten 0 und 1.
|
||||
*/
|
||||
public Vertex[] getVertices(){
|
||||
Vertex[] result = new Vertex[2];
|
||||
result[0] = vertices[0];
|
||||
result[1] = vertices[1];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag setzt das Gewicht der Kante auf pWeight.
|
||||
*/
|
||||
public void setWeight(double pWeight){
|
||||
weight = pWeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert das Gewicht der Kante als double.
|
||||
*/
|
||||
public double getWeight(){
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag setzt die Markierung der Kante auf den Wert pMark.
|
||||
*/
|
||||
public void setMark(boolean pMark){
|
||||
mark = pMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert true, wenn die Markierung der Kante den Wert true hat, ansonsten false.
|
||||
*/
|
||||
public boolean isMarked(){
|
||||
return mark;
|
||||
}
|
||||
|
||||
}
|
||||
312
Graph.java
Normal file
312
Graph.java
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* <p>
|
||||
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
|
||||
* </p>
|
||||
* <p>
|
||||
* Klasse Graph
|
||||
* </p>
|
||||
* <p>
|
||||
* Die Klasse Graph stellt einen ungerichteten, kantengewichteten Graphen dar. Es koennen
|
||||
* Knoten- und Kantenobjekte hinzugefuegt und entfernt, flache Kopien der Knoten- und Kantenlisten
|
||||
* des Graphen angefragt und Markierungen von Knoten und Kanten gesetzt und ueberprueft werden.
|
||||
* Des Weiteren kann eine Liste der Nachbarn eines bestimmten Knoten, eine Liste der inzidenten
|
||||
* Kanten eines bestimmten Knoten und die Kante von einem bestimmten Knoten zu einem
|
||||
* anderen bestimmten Knoten angefragt werden. Abgesehen davon kann abgefragt werden, welches
|
||||
* Knotenobjekt zu einer bestimmten ID gehoert und ob der Graph leer ist.
|
||||
* </p>
|
||||
*
|
||||
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
|
||||
* @version Oktober 2015
|
||||
*/
|
||||
public class Graph{
|
||||
private List<Vertex> vertices;
|
||||
private List<Edge> edges;
|
||||
|
||||
/**
|
||||
* Ein Objekt vom Typ Graph wird erstellt. Der von diesem Objekt
|
||||
* repraesentierte Graph ist leer.
|
||||
*/
|
||||
public Graph(){
|
||||
//Leere Listen fuer Knoten und Kanten erstellen.
|
||||
vertices = new List<Vertex>();
|
||||
edges = new List<Edge>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert eine neue Liste aller Knotenobjekte vom Typ List<Vertex>.
|
||||
*/
|
||||
public List<Vertex> getVertices(){
|
||||
//Eine neue Liste mit allen Vertex-Objekten erstellen.
|
||||
List<Vertex> result = new List<Vertex>();
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess()){
|
||||
result.append(vertices.getContent());
|
||||
vertices.next();
|
||||
}
|
||||
//Aktuelles Element zum Anfang bewegen.
|
||||
result.toFirst();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert eine neue Liste aller Kantenobjekte vom Typ List<Edge>.
|
||||
*/
|
||||
public List<Edge> getEdges(){
|
||||
//Eine neue Liste mit allen Edge-Objekten erstellen.
|
||||
List<Edge> result = new List<Edge>();
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
result.append(edges.getContent());
|
||||
edges.next();
|
||||
}
|
||||
//Aktuelles Element zum Anfang bewegen.
|
||||
result.toFirst();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert das Knotenobjekt mit pID als ID. Ist ein solchen Knotenobjekt nicht im Graphen enthalten,
|
||||
* wird null zurueckgeliefert.
|
||||
*/
|
||||
public Vertex getVertex(String pID){
|
||||
//Vertex-Objekt mit pID als ID suchen.
|
||||
Vertex result = null;
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess() && result == null){
|
||||
if (vertices.getContent().getID().equals(pID)){
|
||||
result = vertices.getContent();
|
||||
}
|
||||
vertices.next();
|
||||
}
|
||||
|
||||
//Objekt zurueckliefern.
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag fuegt den Knoten pVertex in den Graphen ein, sofern es noch keinen
|
||||
* Knoten mit demselben ID-Eintrag wie pVertex im Graphen gibt und pVertex eine ID ungleich null hat.
|
||||
* Ansonsten passiert nichts.
|
||||
*/
|
||||
public void addVertex(Vertex pVertex){
|
||||
//Pruefen, ob der Vertex existiert und eine ID hat.
|
||||
if (pVertex != null && pVertex.getID() != null) {
|
||||
//Pruefen, ob nicht schon ein Vertex mit gleicher ID enthalten ist.
|
||||
boolean freeID = true;
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess() && freeID){
|
||||
if (vertices.getContent().getID().equals(pVertex.getID())){
|
||||
freeID = false;
|
||||
}
|
||||
vertices.next();
|
||||
}
|
||||
|
||||
//Wenn die ID noch frei ist, den Vertex einfuegen, sonst nichts tun.
|
||||
if (freeID) {
|
||||
vertices.append(pVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag fuegt die Kante pEdge in den Graphen ein, sofern beide durch die Kante verbundenen Knoten
|
||||
* im Graphen enthalten sind, nicht identisch sind und noch keine Kante zwischen den Knoten existiert. Ansonsten passiert nichts.
|
||||
*/
|
||||
public void addEdge(Edge pEdge){
|
||||
//Pruefen, ob pEdge exisitert.
|
||||
if (pEdge != null){
|
||||
Vertex[] vertexPair = pEdge.getVertices();
|
||||
|
||||
//Einfuegekriterien pruefen.
|
||||
if (vertexPair[0] != null && vertexPair[1] != null &&
|
||||
this.getVertex(vertexPair[0].getID()) == vertexPair[0] &&
|
||||
this.getVertex(vertexPair[1].getID()) == vertexPair[1] &&
|
||||
this.getEdge(vertexPair[0], vertexPair[1]) == null &&
|
||||
vertexPair[0] != vertexPair[1]){
|
||||
//Kante einfuegen.
|
||||
edges.append(pEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag entfernt den Knoten pVertex aus dem Graphen und loescht alle Kanten, die mit ihm inzident sind.
|
||||
* Ist der Knoten pVertex nicht im Graphen enthalten, passiert nichts.
|
||||
*/
|
||||
public void removeVertex(Vertex pVertex){
|
||||
//Inzidente Kanten entfernen.
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
Vertex[] akt = edges.getContent().getVertices();
|
||||
if (akt[0] == pVertex || akt[1] == pVertex){
|
||||
edges.remove();
|
||||
} else {
|
||||
edges.next();
|
||||
}
|
||||
}
|
||||
|
||||
//Knoten entfernen
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess() && vertices.getContent()!= pVertex){
|
||||
vertices.next();
|
||||
}
|
||||
if (vertices.hasAccess()){
|
||||
vertices.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag entfernt die Kante pEdge aus dem Graphen. Ist die Kante pEdge nicht
|
||||
* im Graphen enthalten, passiert nichts.
|
||||
*/
|
||||
public void removeEdge(Edge pEdge){
|
||||
//Kante aus Kantenliste des Graphen entfernen.
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
if (edges.getContent() == pEdge){
|
||||
edges.remove();
|
||||
} else {
|
||||
edges.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag setzt die Markierungen aller Knoten des Graphen auf pMark.
|
||||
*/
|
||||
public void setAllVertexMarks(boolean pMark){
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess()){
|
||||
vertices.getContent().setMark(pMark);
|
||||
vertices.next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag setzt die Markierungen aller Kanten des Graphen auf pMark.
|
||||
*/
|
||||
public void setAllEdgeMarks(boolean pMark){
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
edges.getContent().setMark(pMark);
|
||||
edges.next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert true, wenn alle Knoten des Graphen mit true markiert sind, ansonsten false.
|
||||
*/
|
||||
public boolean allVerticesMarked(){
|
||||
boolean result = true;
|
||||
vertices.toFirst();
|
||||
while (vertices.hasAccess()){
|
||||
if (!vertices.getContent().isMarked()){
|
||||
result = false;
|
||||
}
|
||||
vertices.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert true, wenn alle Kanten des Graphen mit true markiert sind, ansonsten false.
|
||||
*/
|
||||
public boolean allEdgesMarked(){
|
||||
boolean result = true;
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
if (!edges.getContent().isMarked()){
|
||||
result = false;
|
||||
}
|
||||
edges.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert alle Nachbarn des Knotens pVertex als neue Liste vom Typ List<Vertex>. Hat der Knoten
|
||||
* pVertex keine Nachbarn in diesem Graphen oder ist gar nicht in diesem Graphen enthalten, so
|
||||
* wird eine leere Liste zurueckgeliefert.
|
||||
*/
|
||||
public List<Vertex> getNeighbours(Vertex pVertex){
|
||||
List<Vertex> result = new List<Vertex>();
|
||||
|
||||
//Alle Kanten durchlaufen.
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
|
||||
//Wenn ein Knoten der Kante pVertex ist, den anderen als Nachbarn in die Ergebnisliste einfuegen.
|
||||
Vertex[] vertexPair = edges.getContent().getVertices();
|
||||
if (vertexPair[0] == pVertex) {
|
||||
result.append(vertexPair[1]);
|
||||
} else {
|
||||
if (vertexPair[1] == pVertex){
|
||||
result.append(vertexPair[0]);
|
||||
}
|
||||
}
|
||||
edges.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert eine neue Liste alle inzidenten Kanten zum Knoten pVertex. Hat der Knoten
|
||||
* pVertex keine inzidenten Kanten in diesem Graphen oder ist gar nicht in diesem Graphen enthalten, so
|
||||
* wird eine leere Liste zurueckgeliefert.
|
||||
*/
|
||||
public List<Edge> getEdges(Vertex pVertex){
|
||||
List<Edge> result = new List<Edge>();
|
||||
|
||||
//Alle Kanten durchlaufen.
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess()){
|
||||
|
||||
//Wenn ein Knoten der Kante pVertex ist, dann Kante als inzidente Kante in die Ergebnisliste einfuegen.
|
||||
Vertex[] vertexPair = edges.getContent().getVertices();
|
||||
if (vertexPair[0] == pVertex) {
|
||||
result.append(edges.getContent());
|
||||
} else{
|
||||
if (vertexPair[1] == pVertex){
|
||||
result.append(edges.getContent());
|
||||
}
|
||||
}
|
||||
edges.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert die Kante, welche die Knoten pVertex und pAnotherVertex verbindet,
|
||||
* als Objekt vom Typ Edge. Ist der Knoten pVertex oder der Knoten pAnotherVertex nicht
|
||||
* im Graphen enthalten oder gibt es keine Kante, die beide Knoten verbindet, so wird null
|
||||
* zurueckgeliefert.
|
||||
*/
|
||||
public Edge getEdge(Vertex pVertex, Vertex pAnotherVertex){
|
||||
Edge result = null;
|
||||
|
||||
//Kanten durchsuchen, solange keine passende gefunden wurde.
|
||||
edges.toFirst();
|
||||
while (edges.hasAccess() && result == null){
|
||||
|
||||
//Pruefen, ob die Kante pVertex und pAnotherVertex verbindet.
|
||||
Vertex[] vertexPair = edges.getContent().getVertices();
|
||||
if ((vertexPair[0] == pVertex && vertexPair[1] == pAnotherVertex) ||
|
||||
(vertexPair[0] == pAnotherVertex && vertexPair[1] == pVertex)) {
|
||||
//Kante als Ergebnis merken.
|
||||
result = edges.getContent();
|
||||
}
|
||||
edges.next();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert true, wenn der Graph keine Knoten enthaelt, ansonsten false.
|
||||
*/
|
||||
public boolean isEmpty(){
|
||||
return vertices.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
345
List.java
Normal file
345
List.java
Normal file
@@ -0,0 +1,345 @@
|
||||
/**
|
||||
* <p>
|
||||
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
|
||||
* </p>
|
||||
* <p>
|
||||
* Generische Klasse List<ContentType>
|
||||
* </p>
|
||||
* <p>
|
||||
* Objekt der generischen Klasse List verwalten beliebig viele linear
|
||||
* angeordnete Objekte vom Typ ContentType. Auf hoechstens ein Listenobjekt,
|
||||
* aktuellesObjekt genannt, kann jeweils zugegriffen werden.<br />
|
||||
* Wenn eine Liste leer ist, vollstaendig durchlaufen wurde oder das aktuelle
|
||||
* Objekt am Ende der Liste geloescht wurde, gibt es kein aktuelles Objekt.<br />
|
||||
* Das erste oder das letzte Objekt einer Liste koennen durch einen Auftrag zum
|
||||
* aktuellen Objekt gemacht werden. Ausserdem kann das dem aktuellen Objekt
|
||||
* folgende Listenobjekt zum neuen aktuellen Objekt werden. <br />
|
||||
* Das aktuelle Objekt kann gelesen, veraendert oder geloescht werden. Ausserdem
|
||||
* kann vor dem aktuellen Objekt ein Listenobjekt eingefuegt werden.
|
||||
* </p>
|
||||
*
|
||||
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
|
||||
* @version Generisch_06 2015-10-25
|
||||
*/
|
||||
public class List<ContentType> {
|
||||
|
||||
/* --------- Anfang der privaten inneren Klasse -------------- */
|
||||
|
||||
private class ListNode {
|
||||
|
||||
private ContentType contentObject;
|
||||
private ListNode next;
|
||||
|
||||
/**
|
||||
* Ein neues Objekt wird erschaffen. Der Verweis ist leer.
|
||||
*
|
||||
* @param pContent das Inhaltsobjekt vom Typ ContentType
|
||||
*/
|
||||
private ListNode(ContentType pContent) {
|
||||
contentObject = pContent;
|
||||
next = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Inhalt des Knotens wird zurueckgeliefert.
|
||||
*
|
||||
* @return das Inhaltsobjekt des Knotens
|
||||
*/
|
||||
public ContentType getContentObject() {
|
||||
return contentObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Inhalt dieses Kontens wird gesetzt.
|
||||
*
|
||||
* @param pContent das Inhaltsobjekt vom Typ ContentType
|
||||
*/
|
||||
public void setContentObject(ContentType pContent) {
|
||||
contentObject = pContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Nachfolgeknoten wird zurueckgeliefert.
|
||||
*
|
||||
* @return das Objekt, auf das der aktuelle Verweis zeigt
|
||||
*/
|
||||
public ListNode getNextNode() {
|
||||
return this.next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Verweis wird auf das Objekt, das als Parameter uebergeben
|
||||
* wird, gesetzt.
|
||||
*
|
||||
* @param pNext der Nachfolger des Knotens
|
||||
*/
|
||||
public void setNextNode(ListNode pNext) {
|
||||
this.next = pNext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ----------- Ende der privaten inneren Klasse -------------- */
|
||||
|
||||
// erstes Element der Liste
|
||||
ListNode first;
|
||||
|
||||
// letztes Element der Liste
|
||||
ListNode last;
|
||||
|
||||
// aktuelles Element der Liste
|
||||
ListNode current;
|
||||
|
||||
/**
|
||||
* Eine leere Liste wird erzeugt.
|
||||
*/
|
||||
public List() {
|
||||
first = null;
|
||||
last = null;
|
||||
current = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert den Wert true, wenn die Liste keine Objekte enthaelt,
|
||||
* sonst liefert sie den Wert false.
|
||||
*
|
||||
* @return true, wenn die Liste leer ist, sonst false
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
// Die Liste ist leer, wenn es kein erstes Element gibt.
|
||||
return first == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert den Wert true, wenn es ein aktuelles Objekt gibt,
|
||||
* sonst liefert sie den Wert false.
|
||||
*
|
||||
* @return true, falls Zugriff moeglich, sonst false
|
||||
*/
|
||||
public boolean hasAccess() {
|
||||
// Es gibt keinen Zugriff, wenn current auf kein Element verweist.
|
||||
return current != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls die Liste nicht leer ist, es ein aktuelles Objekt gibt und dieses
|
||||
* nicht das letzte Objekt der Liste ist, wird das dem aktuellen Objekt in
|
||||
* der Liste folgende Objekt zum aktuellen Objekt, andernfalls gibt es nach
|
||||
* Ausfuehrung des Auftrags kein aktuelles Objekt, d.h. hasAccess() liefert
|
||||
* den Wert false.
|
||||
*/
|
||||
public void next() {
|
||||
if (this.hasAccess()) {
|
||||
current = current.getNextNode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls die Liste nicht leer ist, wird das erste Objekt der Liste aktuelles
|
||||
* Objekt. Ist die Liste leer, geschieht nichts.
|
||||
*/
|
||||
public void toFirst() {
|
||||
if (!isEmpty()) {
|
||||
current = first;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls die Liste nicht leer ist, wird das letzte Objekt der Liste
|
||||
* aktuelles Objekt. Ist die Liste leer, geschieht nichts.
|
||||
*/
|
||||
public void toLast() {
|
||||
if (!isEmpty()) {
|
||||
current = last;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
|
||||
* aktuelle Objekt zurueckgegeben, andernfalls (hasAccess() == false) gibt
|
||||
* die Anfrage den Wert null zurueck.
|
||||
*
|
||||
* @return das aktuelle Objekt (vom Typ ContentType) oder null, wenn es
|
||||
* kein aktuelles Objekt gibt
|
||||
*/
|
||||
public ContentType getContent() {
|
||||
if (this.hasAccess()) {
|
||||
return current.getContentObject();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls es ein aktuelles Objekt gibt (hasAccess() == true) und pContent
|
||||
* ungleich null ist, wird das aktuelle Objekt durch pContent ersetzt. Sonst
|
||||
* geschieht nichts.
|
||||
*
|
||||
* @param pContent
|
||||
* das zu schreibende Objekt vom Typ ContentType
|
||||
*/
|
||||
public void setContent(ContentType pContent) {
|
||||
// Nichts tun, wenn es keinen Inhalt oder kein aktuelles Element gibt.
|
||||
if (pContent != null && this.hasAccess()) {
|
||||
current.setContentObject(pContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird ein neues
|
||||
* Objekt vor dem aktuellen Objekt in die Liste eingefuegt. Das aktuelle
|
||||
* Objekt bleibt unveraendert. <br />
|
||||
* Wenn die Liste leer ist, wird pContent in die Liste eingefuegt und es
|
||||
* gibt weiterhin kein aktuelles Objekt (hasAccess() == false). <br />
|
||||
* Falls es kein aktuelles Objekt gibt (hasAccess() == false) und die Liste
|
||||
* nicht leer ist oder pContent gleich null ist, geschieht nichts.
|
||||
*
|
||||
* @param pContent
|
||||
* das einzufuegende Objekt vom Typ ContentType
|
||||
*/
|
||||
public void insert(ContentType pContent) {
|
||||
if (pContent != null) { // Nichts tun, wenn es keinen Inhalt gibt.
|
||||
if (this.hasAccess()) { // Fall: Es gibt ein aktuelles Element.
|
||||
|
||||
// Neuen Knoten erstellen.
|
||||
ListNode newNode = new ListNode(pContent);
|
||||
|
||||
if (current != first) { // Fall: Nicht an erster Stelle einfuegen.
|
||||
ListNode previous = this.getPrevious(current);
|
||||
newNode.setNextNode(previous.getNextNode());
|
||||
previous.setNextNode(newNode);
|
||||
} else { // Fall: An erster Stelle einfuegen.
|
||||
newNode.setNextNode(first);
|
||||
first = newNode;
|
||||
}
|
||||
|
||||
} else { //Fall: Es gibt kein aktuelles Element.
|
||||
|
||||
if (this.isEmpty()) { // Fall: In leere Liste einfuegen.
|
||||
|
||||
// Neuen Knoten erstellen.
|
||||
ListNode newNode = new ListNode(pContent);
|
||||
|
||||
first = newNode;
|
||||
last = newNode;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls pContent gleich null ist, geschieht nichts.<br />
|
||||
* Ansonsten wird ein neues Objekt pContent am Ende der Liste eingefuegt.
|
||||
* Das aktuelle Objekt bleibt unveraendert. <br />
|
||||
* Wenn die Liste leer ist, wird das Objekt pContent in die Liste eingefuegt
|
||||
* und es gibt weiterhin kein aktuelles Objekt (hasAccess() == false).
|
||||
*
|
||||
* @param pContent
|
||||
* das anzuhaengende Objekt vom Typ ContentType
|
||||
*/
|
||||
public void append(ContentType pContent) {
|
||||
if (pContent != null) { // Nichts tun, wenn es keine Inhalt gibt.
|
||||
|
||||
if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
|
||||
this.insert(pContent);
|
||||
} else { // Fall: An nicht-leere Liste anfuegen.
|
||||
|
||||
// Neuen Knoten erstellen.
|
||||
ListNode newNode = new ListNode(pContent);
|
||||
|
||||
last.setNextNode(newNode);
|
||||
last = newNode; // Letzten Knoten aktualisieren.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Falls es sich bei der Liste und pList um dasselbe Objekt handelt,
|
||||
* pList null oder eine leere Liste ist, geschieht nichts.<br />
|
||||
* Ansonsten wird die Liste pList an die aktuelle Liste angehaengt.
|
||||
* Anschliessend wird pList eine leere Liste. Das aktuelle Objekt bleibt
|
||||
* unveraendert. Insbesondere bleibt hasAccess identisch.
|
||||
*
|
||||
* @param pList
|
||||
* die am Ende anzuhaengende Liste vom Typ List<ContentType>
|
||||
*/
|
||||
public void concat(List<ContentType> pList) {
|
||||
if (pList != this && pList != null && !pList.isEmpty()) { // Nichts tun,
|
||||
// wenn pList und this identisch, pList leer oder nicht existent.
|
||||
|
||||
if (this.isEmpty()) { // Fall: An leere Liste anfuegen.
|
||||
this.first = pList.first;
|
||||
this.last = pList.last;
|
||||
} else { // Fall: An nicht-leere Liste anfuegen.
|
||||
this.last.setNextNode(pList.first);
|
||||
this.last = pList.last;
|
||||
}
|
||||
|
||||
// Liste pList loeschen.
|
||||
pList.first = null;
|
||||
pList.last = null;
|
||||
pList.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wenn die Liste leer ist oder es kein aktuelles Objekt gibt (hasAccess()
|
||||
* == false), geschieht nichts.<br />
|
||||
* Falls es ein aktuelles Objekt gibt (hasAccess() == true), wird das
|
||||
* aktuelle Objekt geloescht und das Objekt hinter dem geloeschten Objekt
|
||||
* wird zum aktuellen Objekt. <br />
|
||||
* Wird das Objekt, das am Ende der Liste steht, geloescht, gibt es kein
|
||||
* aktuelles Objekt mehr.
|
||||
*/
|
||||
public void remove() {
|
||||
// Nichts tun, wenn es kein aktuelle Element gibt oder die Liste leer ist.
|
||||
if (this.hasAccess() && !this.isEmpty()) {
|
||||
|
||||
if (current == first) {
|
||||
first = first.getNextNode();
|
||||
} else {
|
||||
ListNode previous = this.getPrevious(current);
|
||||
if (current == last) {
|
||||
last = previous;
|
||||
}
|
||||
previous.setNextNode(current.getNextNode());
|
||||
}
|
||||
|
||||
ListNode temp = current.getNextNode();
|
||||
current.setContentObject(null);
|
||||
current.setNextNode(null);
|
||||
current = temp;
|
||||
|
||||
//Beim loeschen des letzten Elements last auf null setzen.
|
||||
if (this.isEmpty()) {
|
||||
last = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert den Vorgaengerknoten des Knotens pNode. Ist die Liste leer, pNode
|
||||
* == null, pNode nicht in der Liste oder pNode der erste Knoten der Liste,
|
||||
* wird null zurueckgegeben.
|
||||
*
|
||||
* @param pNode
|
||||
* der Knoten, dessen Vorgaenger zurueckgegeben werden soll
|
||||
* @return der Vorgaenger des Knotens pNode oder null, falls die Liste leer ist,
|
||||
* pNode == null ist, pNode nicht in der Liste ist oder pNode der erste Knoten
|
||||
* der Liste ist
|
||||
*/
|
||||
private ListNode getPrevious(ListNode pNode) {
|
||||
if (pNode != null && pNode != first && !this.isEmpty()) {
|
||||
ListNode temp = first;
|
||||
while (temp != null && temp.getNextNode() != pNode) {
|
||||
temp = temp.getNextNode();
|
||||
}
|
||||
return temp;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
77
Vertex.java
Normal file
77
Vertex.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* <p>
|
||||
* Materialien zu den zentralen NRW-Abiturpruefungen im Fach Informatik ab 2018
|
||||
* </p>
|
||||
* <p>
|
||||
* Klasse Vertex
|
||||
* </p>
|
||||
* <p>
|
||||
* Die Klasse Vertex stellt einen einzelnen Knoten eines Graphen dar. Jedes Objekt
|
||||
* dieser Klasse verfuegt ueber eine im Graphen eindeutige ID als String und kann diese
|
||||
* ID zurueckliefern. Darueber hinaus kann eine Markierung gesetzt und abgefragt werden.
|
||||
* </p>
|
||||
*
|
||||
* @author Qualitaets- und UnterstuetzungsAgentur - Landesinstitut fuer Schule
|
||||
* @version Oktober 2015
|
||||
*/
|
||||
public class Vertex{
|
||||
//Einmalige ID des Knotens und Markierung
|
||||
private String id;
|
||||
private boolean mark;
|
||||
|
||||
// Ergänzugnen für Dijkstra
|
||||
// Speicher für den kürzesten Weg
|
||||
private double value;
|
||||
// Vorgängenknoten im kürzesten Weg
|
||||
private Vertex predecessor;
|
||||
|
||||
/**
|
||||
* Ein neues Objekt vom Typ Vertex wird erstellt. Seine Markierung hat den Wert false.
|
||||
*/
|
||||
public Vertex(String pID){
|
||||
id = pID;
|
||||
mark = false;
|
||||
value = Integer.MAX_VALUE;
|
||||
predecessor = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert die ID des Knotens als String.
|
||||
*/
|
||||
public String getID(){
|
||||
return new String(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Auftrag setzt die Markierung des Knotens auf den Wert pMark.
|
||||
*/
|
||||
public void setMark(boolean pMark){
|
||||
mark = pMark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Anfrage liefert true, wenn die Markierung des Knotens den Wert true hat, ansonsten false.
|
||||
*/
|
||||
public boolean isMarked(){
|
||||
return mark;
|
||||
}
|
||||
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Vertex getPredecessor() {
|
||||
return predecessor;
|
||||
}
|
||||
|
||||
public void setPredecessor(Vertex predecessor) {
|
||||
this.predecessor = predecessor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
59
package.bluej
Normal file
59
package.bluej
Normal file
@@ -0,0 +1,59 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=BinarySearchTree
|
||||
dependency1.to=ComparableContent
|
||||
dependency1.type=UsesDependency
|
||||
dependency2.from=Racingsimulator
|
||||
dependency2.to=Athlete
|
||||
dependency2.type=UsesDependency
|
||||
editor.fx.0.height=738
|
||||
editor.fx.0.width=816
|
||||
editor.fx.0.x=466
|
||||
editor.fx.0.y=146
|
||||
objectbench.height=100
|
||||
objectbench.width=1256
|
||||
package.divider.horizontal=0.6
|
||||
package.divider.vertical=0.881243063263041
|
||||
package.editor.height=787
|
||||
package.editor.width=1145
|
||||
package.editor.x=0
|
||||
package.editor.y=0
|
||||
package.frame.height=1000
|
||||
package.frame.width=1296
|
||||
package.numDependencies=2
|
||||
package.numTargets=4
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.height=58
|
||||
readme.name=@README
|
||||
readme.width=47
|
||||
readme.x=10
|
||||
readme.y=10
|
||||
target1.height=50
|
||||
target1.name=Racingsimulator
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.width=130
|
||||
target1.x=450
|
||||
target1.y=472
|
||||
target2.height=50
|
||||
target2.name=ComparableContent
|
||||
target2.showInterface=false
|
||||
target2.type=InterfaceTarget
|
||||
target2.width=250
|
||||
target2.x=160
|
||||
target2.y=270
|
||||
target3.height=50
|
||||
target3.name=Athlete
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.width=90
|
||||
target3.x=280
|
||||
target3.y=550
|
||||
target4.height=50
|
||||
target4.name=BinarySearchTree
|
||||
target4.showInterface=false
|
||||
target4.type=ClassTarget
|
||||
target4.width=230
|
||||
target4.x=370
|
||||
target4.y=90
|
||||
Reference in New Issue
Block a user