Datenstrukturen

Die vorliegenden Materialien wurden von Daniel Hoherz und André Tempel erstellt. Sollten andere Editoren die Materialien erstellt haben, werden diese explizit genannt.

Der Veranstalter Pyromania ist auf große Feuerwerkeffekte spezialisiert. Eine Stadt möchte ein Fest mit abschließendem Feuerwerk ausrichten und hat Pyromania engagiert, ein Feuerwerk auf einer Wasserplatform zu planen und durchzuführen.
Die Feuerwerksrakete werden in ein kleine Abschussschächte gesteckt, welche in einer langen Reihe miteinander verbunden sind. Die Raketen werden mit Kabeln mit einer Steuereinheit verbunden, welche ein vorher festgelegtes Programm zum Zünden der einzelnen Raketen ablaufen lässt.
Eine dieser Abschussvorrichtungen sieht zum Beispiel so aus:

KI-erzeugte Darstellung der Abschussvorrichtung

Wir betrachten zunächst nur eine Reihe der Anlage.
Eine Klasse Feuerwerk wird benötigt, welche unter anderem ein Attribut erhalten soll, in welchem gespeichert ist, was genau an welchem Platz in der Raketenreihe ist. Für ein konkretes Beispiel sollen diese sein: Rot, Grün, Grün und Blau.

Tauschen Sie sich mit Ihrem Sitznachbarn aus, wie Sie die Anlage algorithmisch speichern und verwalten könnten.

In der Informatik ist eine Datenstruktur ein Objekt, welches zur Speicherung und Organisation von Daten dient. Dabei handelt es sich um eine Struktur, weil die Daten in einer bestimmten Art und Weise angeordnet und verknüpft werden, um den Zugriff auf sie und ihre Verwaltung effizient zu ermöglichen. Wie bereits raus zu lesen ist, unterscheiden sich die Datenstrukturen wie sie strukturiert sind und welche Operationen es gibt, um einen Zugriff auf sie zu ermöglichen. So gibt es für verschieden Anwendungsbereich verschiedene Datenstrukturen, welche jeweils besser geeignet sind. Es werden die beiden Varianten statische Datenstruktur und dynamische Datenstruktur unterschieden.

Statische Datenstruktur Dynamische Datenstruktur
Beispiele int, boolean, char, String, double, Reihung, mehrdimensionale Reihung, … Stack, Queue, Dynamische Reihung…
Eigenschaften Feste Größe Größe der Datenstruktur und damit die Menge der speicherbaren Daten ist variabel, dynamisch

Die einfachste Form der Datenstrukturen haben wir bereits kennengelernt und vielfach genutzt – Variablen sogenannter primitiver Datentypen, wie z. B. integer, character, boolean…(Ein String ist kein primitiver Datentyp!).
Die Definition oben besagt, dass Datenstrukturen zur Speicherung von Daten dienen und diesen Sachverhalt haben die uns bisher bekannten Variablen erfüllt. Ebenso besagt die Definition, dass festgelegt wird, in welcher Art die Daten gespeichert werden. Auch diese wird bei Variablen festgelegt, indem gesagt wird, dass eine Variable von einem bestimmten Datentyp gespeichert wird. Darüber hinaus wird durch den Datentyp festgelegt, welche Operationen auf ihr möglich sind, womit dann auch der Zugriff geregelt wird.
Doch warum stehen die Variablen unter den statischen Datenstrukturen? Variablen sind statische Datenstrukturen, da ihre Größe, also wie viele Werte gespeichert werden können, immer gleich ist und nicht veränderlich sind.
Doch schauen wir uns mal andere statische Datenstrukturen an, die etwas mehr können, als eine Variable.

Rot Grün Grün Blau
0 1 2 3

Bei dieser Datenstruktur gibt es eine vorher festgelegte Anzahl an Feldern, in welche man eine Variable mit vorher festgelegtem Datentyp eintragen kann. Weiterhin sind die einzelnen Felder indiziert, beginnend mit dem Index 0.
Mit dieser Struktur sind einige Anforderungen an die Anlage deutlich einfacher umzusetzen. Die maximale Anzahl der Raketen ist direkt durch die vorher festgelegte Anzahl der Felder bekannt. Außerdem kann man die Felder der Reihe nach in einer beliebigen Formatierung ausgeben. Sucht man eine bestimmte Rakete, kann man die Felder schrittweise von vorne beginnend durchgehen und jeden Eintrag mit der gesuchten Farbe vergleichen. Wird ein Rakete enfernt, kann man diese suchen und aus dem entsprechenden Feld enfernen. Ebenso kann an eine freie Stelle direkt eine Rakete eingetragen werden, da man die Indizes einer freien Stelle suchen oder vorher speichern kann. Eine Erweiterung der Anlage ist ebenso möglich. Man erzeugt eine analoge Datenstruktur dieser Art, welche um die Anzahl der zusätzlichen Raketen größer ist, überträgt die bisherigen Raketen schrittweise in die neue Struktur und ergänzt schließlich die freien Felder mit den neuen Raketen.
Diese Art der Datenstruktur heißt eindimensionale Reihung bzw. eindimensionales Array, oder auch nur kurz Reihung bzw. Array.
Eine Reihung (Array/Feld) ist eine geordnete Sammlung von Werten. Die Werte können primitive Datentypen oder Objekte beliebiger anderer Klassen sein, aber alle Werte in einem Array müssen vom gleichen Typ sein.
Im Folgenden ist eine Übersicht über die Nutzung von Reihungen in Java gegeben.

Deklarieren einer Reihung
Wenn wir die oben abgebildete Reihung deklarieren wollen, dann wird das in Java so gemacht:

Deklarieren Erklärung
String[] test; String [ ] test ;
Datentyp der Objekte in der Reihung Klammern Name des Arrays Ende der Anweisung

Initialisieren einer Reihung
Bisher wurde nur Speicherplatz für die Reihung test reserviert, in welche nur Zeichenketten gespeichert werden dürfen. Damit der Computer weiß, wie groß die Reihung sein soll, muss man sie initialisieren:

Initialisieren Erklärung
test=new String[4]; test = new String[4]
Name der Reihung Wertzuweisung Konstruktor einer Reihung und Angabe der Größe

Jetzt existiert eine leere Reihung der Größe 4, in welcher Zeichenketten gespeichert werden dürfen. Aktuell ist die Reihung aber leer. Das bedeutet, dass an allen Positionen null eingetragen ist.

test: null null null null
Index: 0 1 2 3

Man kann auch beide Schritte gleichzeitig machen, wenn die Größe der Reihung direkt angegeben werden soll:

Deklarieren + Initialisieren Erklärung
String[] test=new String[4]; String[] test = new String[4]
Deklarieren Wertzuweisung Initialisieren

Werte in einer Reihung speichern
Nun sollen die Raketen aus dem Einstiegsbeispiel in unserer Reihung gespeichert werden. Wir starten mit der ersten Rakete, „Rot“ am Index 0:

Speichern Erklärung
test[0] = „Rot“; test [0] = „Rot“
Name der Reihung Index, der angesprochen werden soll Wertzuweisung Neuer Wert, hier ein String

Man kann auch direkt Deklarieren, Initialisieren und Werte zuweisen, sollte man dies wollen bzw. können:

Alles zusammen Erklärung
String[] test = {„Rot“,“Grün“,“Grün“,“Blau“}; String[] test = {„Rot“,“Grün“,“Grün“,“Blau“}
Deklarieren Wertzuweisung Werte direkt speichern

Werte aus Reihung auslesen
Wenn eine Reihung vorhanden ist und man sich für deren Inhalte interessiert, kann man auch die gespeicherten Objekte abfragen. Man kann so entweder Einträge der Reihung in anderen Variablen speichern oder sich einfach einen Wert ausgeben lassen. Mit einer Schleife kann man sich auch alle Elemente der Reihung ausgeben:

Ein Wert aus test in einer Variablen speichern Erklärung
String s;
s = test[1];
s = test[1]
Wo gespeichert wird Der Wert am Index 1, hier „Grün“, wird zurückgegeben

Einen Wert aus test ausgeben Erklärung
print(test[4]); print(…) test[4]
Etwas auf der Konsole anzeigen Der Wert am Index 4, hier „Blau“, wird zurückgegeben

Alle Werte in test ausgeben Erklärung
for(int i=0; i<4; i++) {
print(test[i]);
}
for(int i=0; i<4; i++) print(…) test[i]
Schleife, die 4-mal durchläuft Etwas auf der Konsole anzeigen Der Wert am aktuellen Index i, wird zurückgegeben

Länge der Reihung angeben
Man kann sich auch die Länge einer Reihung zurückgeben lassen:

Länge von test nutzen Erklärung
int a = test.length; int a = test.length
Ganzzahlvariable a deklarieren und einen Wert zuweisen Länge von test, hier 4, wird zurückgegeben

Dies kann man nutzen, um die gesamte Reihung wie weiter oben zu durchlaufen, wenn deren Länge nicht bekannt ist.

Alle Werte in test ausgeben Erklärung
for(int i=0; i<test.length; i++) {
print(test[i]);
}
for(int i=0; i<test.length; i++) print(test[i])
Schleife, die so oft durchläuft, wie die Reihe groß ist Auf der Konsole den Wert von test am aktuellen Index ausgeben

Achtung: Wenn ein negativer Index oder ein Index, der größer ist, als das letzte Element der Reihung, eingegeben wird, dann wird eine IndexOutOfBoundsException ausgelöst, die angibt, dass man den Index-Bereich/die Größe der Reihung verlassen hat.

  1. Um zunächst ein wenig den Umgang mit Reihungen kennenzulernen sind im folgenden die wichtigsten Informationen zu Reihungen in Java an konkreten Beispielen vorgegeben. Nutzen Sie schrittweise die Erklärungen (oben), um die folgenden Aufgaben zu bearbeiten.
  2. Deklarieren und initialisieren Sie auf je zwei verschiedene Weisen die folgenden Reihungen in einer neuen Datei mit dem Namen Reihuung in Java.

    • eine Zeichenkettenreihung mit den Werten haus, auto, baum, pflanze, katze, maus
    • eine Ganzzahlreihung mit den Werten 4, 32, -1, 79, 13
    • eine char-Reihung mit den Werten a, b, c, d, e, f, g

    Ersetzen Sie in jeder Ihrer Reihungen einen beliebigen Wert durch einen anderen.

    Lassen Sie sich nacheinander die Werte einer der drei Reihungen vollständig ausgeben und zwar in mindestens drei Varianten:

    • Ein Wert pro Zeile,
    • Alle Werte in einer Zeile,
    • Es wird nur jeder zweite Wert ausgeben,
    • Alle Werte sollen rückwärts, also von hinten nach vorne, ausgegeben werden.

  1. (gA) Um den Umgang mit Reihungen noch etwas aufzufrischen und einzuüben, kommen hier einige Aufgaben auf einer Reihungen vom Inhaltstyp Ganzzahl.
  2. Deklarieren Sie eine Ganzzahl-Reihungen und weisen Sie ihr 20 zufällige Zahlen zwischen 0 und 40 zu. Hinweis: Dies können Sie mithilfe einer for-Schleife machen.

    Nachdem die Reihung mit Werten gefüllt ist, lassen Sie sich diese ausgeben.
    Implementieren Sie eine Ausgabe der Werte der Reihung.

    Nun sollen die Werte summiert werden und die Summe soll ausgeben werden.

    Implementieren Sie nun, dass der Mittelwert der Werte gebildet wird.

    Es soll die Anzahl an Werten gezählt werden, die über 30 liegen.

  1. Die Klasse Feuerwerk soll zunächst wie unten abgebildet umgesetzt werden.
    Das Attribut anlage ist eine Zeichenkettenreihung, welche die Raketen in Form von Zeichenketten (die jeweilige Farbe) speichern soll. Das Attribut laenge ist die Länge der Anlage.
    Dem Konstruktor wird eine Zeichenkettenreihung übergeben, welche als anlage gespeichert werden soll. Das Attribut laenge soll vom Konstruktor auf die Länge von anlage gesetzt werden. Die get-Operation sollen die Länge der Anlage, eine Rakete an einer bestimmten Stelle und die gesamte Anlage zurückgeben.
    Die set-Operation soll an den übergebenen Index die übergebene Rakete platzieren.

    Klassenkarte der Klasse Feuerwerk

    Entscheiden Sie, ob Sie die Basis- oder die Challenge-Variante umsetzen wollen. Sie können auch jederzeit zwischen diesen wechseln.

  2. Die Klassen Feuerwerk und FeuerwerkTest wurden Ihnen in der Online IDE bereitgestellt.

    Führen Sie die Klasse FeuerwerkTest aus.
    Erläutern Sie die Funktionsweise der Operationen public Feuerwerk(String[] s), public int getLaenge() und der Anweisungen in der Klasse FeuerwerkTest.

    Die weiteren in der Klassenkarte abgebildeten Operationen haben die folgende Funktionsweise:

    • getRakete soll die am übergebenen Index a gespeicherte Rakete zurückgeben.
    • getAnlage soll die gesamte Anlage zurückgeben.
    • setRakete soll die Rakete am übergebenen Index b mit der Zeichenkette r überschreiben.
    • (Ergänzung) showAnlage soll die komplette Anlage als Zeichenkettenreihung zurückgegeben.

    Schauen Sie sich die noch nicht gefüllten Operationen in der Klasse Feuerwerk an und erläutern Sie die einzelnen Elemente der Methodenköpfe. Gehen Sie hierbei insbesondere auf Übergabeparameter und Rückgabewerte ein.

    Vervollständigen Sie die Operationen getRakete, getAnlage und setRakete.
    Testen Sie die get-Operationen.
    Ersetzen Sie die blaue Rakete durch eine weiße Rakete.

    Eine weitere get-Operation soll ergänzt werden. Die Operation getRakete(String c) erhält eine Farbe als Zeichenkette übergeben und soll den ersten Index zurückgeben, an dem sich eine Rakete der gesuchten Farbe befindet. Nun wird es zwei Operationen getRakete geben, die sich im Übergabeparameter unterscheiden. Der Computer wählt, je nach Datentyp des Übergabeparameters die entsprechende Operation aus. Das nennt man Überladen von Operationen.
    Implementieren Sie die neue Operation getRakete.

    Die Operation hatFarbe soll überprüfen, ob eine übergebene Farbe in der Reihung anlage enthalten ist und in diesem Fall true zurückgeben, anderenfalls false.
    Implementieren Sie die Operation hatFarbe.

    Die Operation changeRakete(int i1, int i2) soll in der Reihung anlage die Raketen an den beiden übergebenen Positionen tauschen.
    Implementieren Sie die Operation changeRakete.

    Nun soll es die Möglichkeit geben, die Flughöhe der Raketen der Reihung anlage zu speichern. Hierfür erhält die Klasse Feuerwerk eine zweites Attribut hoehe vom Datentyp Ganzzahlreihung.
    Ergänzen Sie Ihre Klasse Feuerwerk um das Attribut hoehe. Verändern Sie den bisherigen Konstruktor so, dass er die Werte in hoehe auf 0 setzt.
    Implementieren Sie einen zweiten Konstruktor, dem zusätzlich zur Zeichenkettenreihung eine Ganzzahlreihung übergeben wird, welche unter dem Attribut hoehe gespeichert werden soll.
    Erstellen Sie nun ein Objekt der Klasse Feuerwerk mit den Raketen Rot, Gruen, Blau, Rot und den Flughöhen 100, 80, 120, 90.

    Implementieren Sie eine Operation minHoehe, welche den Index des kleinsten Wertes der Flughöhe ermittelt und zurückgibt. Machen Sie dieses nochmal für maxHoehe.
    Zusatz: Implementieren Sie eine Operation ordneHöhe, welche den kleinsten Wert der Flughöhe an den Index 0 tauscht.

    Optimieren Sie Ihre Operationen, sodass nicht sinnvolle Eingaben abgefangen werden.
    Beispielsweise könnte man bei getRakete(int a) einen Index eingeben, den es nicht gibt.

    Implementieren Sie die Klasse Feuerwerk wie oben beschrieben.

    Erstellen Sie eine Testklasse, in welcher Sie ein Objekt der Klasse Feuerwerk mit den Raketen Rot, Gruen, Blau, Rot anlegen.
    Testen Sie die get-Operationen.
    Ersetzen Sie die blaue Rakete durch eine weiße Rakete.

    Eine weitere get-Operation soll ergänzt werden. Die Operation getRakete(String c) erhält eine Farbe als Zeichenkette übergeben und soll den ersten Index zurückgeben, an dem sich eine Rakete der gesuchten Farbe befindet. Nun wird es zwei Operationen getRakete geben, die sich im Übergabeparameter unterscheiden. Der Computer wählt, je nach Datentyp des Übergabeparameters die entsprechende Operation aus. Das nennt man Überladen von Operationen.
    Implementieren Sie die neue Operation getRakete.

    Die Operation hatFarbe soll überprüfen, ob eine übergebene Farbe in der Reihung anlage enthalten ist und in diesem Fall true zurückgeben, anderenfalls false.
    Implementieren Sie die Operation hatFarbe.

    Die Operation changeRakete(int i1, int i2) soll in der Reihung anlage die Raketen an den beiden übergebenen Positionen tauschen.
    Implementieren Sie die Operation changeRakete.

    Nun soll es die Möglichkeit geben, die Flughöhe der Raketen der Reihung anlage zu speichern. Hierfür erhält die Klasse Feuerwerk eine zweites Attribut hoehe vom Datentyp Ganzzahlreihung.
    Ergänzen Sie Ihre Klasse Feuerwerk um das Attribut hoehe. Verändern Sie den bisherigen Konstruktor so, dass er die Werte in hoehe auf 0 setzt.
    Implementieren Sie einen zweiten Konstruktor, dem zusätzlich zur Zeichenkettenreihung eine Ganzzahlreihung übergeben wird, welche unter dem Attribut hoehe gespeichert werden soll.
    Erstellen Sie nun ein Objekt der Klasse Feuerwerk mit den Raketen Rot, Gruen, Blau, Rot und den Flughöhen 100, 80, 120, 90.

    Implementieren Sie eine Operation minHoehe, welche den Index des kleinsten Wertes der Flughöhe ermittelt und zurückgibt. Machen Sie dieses nochmal für maxHoehe.
    Zusatz: Implementieren Sie eine Operation ordneHöhe, welche den kleinsten Wert der Flughöhe an den Index 0 tauscht.

  1. Vergleichen Sie in einer eigenen Testklasse die beiden folgenden Algorithmen:
    int[] zahlen = {1,-2,3,-4,5,-6,7,-8,9,-10};
    for (int i = 0; i < zahlen.length; i++) {
        print(zahlen[i] + " ");
    }
    
    
    int[] zahlen = {1,-2,3,-4,5,-6,7,-8,9,-10};
    for (int zahl : zahlen) {
        print(zahl + " ");
    }
    
    
    Die linke Variante ist eine bekannte for-Schleife. Die rechte Variante ist eine erweiterte for-Schleife, auch for-each-Schleife genannt. Diese kann man alternativ zu einer for-Schleife für alle Arrays nutzen.
  1. Ein Objekt der Klasse Color ist eine Farbe in RGB-Codierung. Hierbei werden die Intensitäten der Grundfarben Rot, Grün und Blau in jeweils ganzzahligen Werten im Bereich von 0 bis 255 angegeben. Beispiele für konkrete Farben finden Sie unten.

    Farbe HTML-Name RGB-Dezimalcodierung
    Weiß (255,255,255)
    Schwarz (0,0,0)
    Rot (255,0,0)
    Limette (0,255,0)
    Blau (0,0,255)
    Gelb (255,255,0)
    Cyan (0,255,255)
    Magenta (255,0,255)
    Grün (0,128,0)
    Lila (128,0,128)
    Beispiele für die RGB-Codierung von Farben

    Die Klasse Feuerwerk wird um die Klasse Color und andere Funktionalitäten erweitert, siehe unten. Der Datentyp der Reihung anlage wird dadurch von Zeichenkette zu Color. In einer Challenge-Variante wird zusätzlich noch eine Klasse Rakete genutzt, wodurch der Datentyp der Reihung anlage von Zeichenkette zu Rakete wird.
    Entscheiden Sie sich zu Beginn dieser Aufgabe für eine der beiden Varianten. Die Klasse Color liegt bereits vor und kann entspechend genutzt werden.

  2. Klassenkarte der Klassen Feuerwerk und Color

    Verändern Sie Ihre bisherige Klasse Feuerwerk entsprechend der neuen Situation.

    Ist ein Platz der Reihung anlage nicht besetzt, wird eine Rakete mit der Farbe Schwarz mit Flughöhe 0 an die freie Stelle gesetzt.
    Verändern Sie die Konstruktoren entsprechend.
    Erstellen Sie ein neues Objekt der Klasse Feuerwerk und belegen Sie die Reihung anlage mit Raketen der Farben Rot, Blau, freier Platz, Grün, Blau, in der angegebenen Reihenfolge.

    Die Operation setRakete soll nun eine Rakete an einen bestimmten Platz setzen, wenn dieser Platz nicht besetzt ist.
    Verändern Sie die Operation setRakete nach diesen Vorgaben.
    Testen Sie die Operation, indem Sie eine weiße Rakete an die freie Stelle setzen.

    Die Operation zuenden der Klasse Feuerwerk erhält eine Reihung aus Farben als Übergabeparameter und soll zählen, wie viele Raketen der jeweiligen Farben in der Reihung anlage enthalten sind und deren Anzahlen in einer Ganzzahlreihung zurückgeben.
    Implementieren Sie die Operation zuenden.
    Testen Sie die Operation für rote und blaue Raketen.

    Klassenkarte der Klassen Feuerwerk und Color

    Implementieren Sie die Klasse Rakete und nutzen Sie die Klasse Color entsprechend.

    Verändern Sie Ihre bisherige Klasse Feuerwerk entsprechend der neuen Situation.

    Ist ein Platz der Reihung anlage nicht besetzt, wird eine Rakete mit der Farbe Schwarz und 0 für die anderen Attributswerte an die freie Stelle gesetzt.
    Verändern Sie den Konstruktor entsprechend.
    Erstellen Sie ein neues Objekt der Klasse Feuerwerk und belegen Sie die die Reihung anlage mit Raketen der Farben Rot, Blau, freier Platz, Grün, Blau, in der angegebenen Reihenfolge.
    Das Attribut laenge soll einen beliebigen Wert zwischen 10.0 und 25.0 haben, zdauer zwischen 1.0 und 3.0 und hoehe zwischen 20 und 60.

    Die Operation setRakete soll nun ein Objekt der Klasse Rakete an einen bestimmten Platz setzen, wenn dieser Platz nicht besetzt ist. Ist der Platz jedoch besetzt, soll die neue Rakete an den darauf folgenden Platz gesetzt und die restlichen Plätze nach hinten geschoben werden. Gegebenenfalls muss die letzte Rakete enfernt werden.
    Verändern Sie die Operation setRakete nach diesen Vorgaben.
    Testen Sie die Operation, indem Sie eine weiße Rakete am Index 1 einfügen.

    Die Operation zuenden der Klasse Feuerwerk erhält eine Reihung aus Farben und eine Ganzzahl als Übergabeparameter. Sie soll zählen, wie viele Raketen der jeweiligen Farben in der Reihung anlage enthalten sind und gleichzeitig mindestens die übergebene Flughöhe erreichen und deren Anzahlen in einer Ganzzahlreihung zurückgeben.
    Implementieren Sie die Operation zuenden.
    Testen Sie die Operation für rote und blaue Raketen und einer Flughöhe von 40.

  1. Nun soll es um eine Wetterstation gehen.
    Die Wetterstation hat für 14 Tage die folgenden Temperaturen aufgezeichnet.
    Tag 1 2 3 4 5 6 7 8 9 10 11 12 13 14
    Temperatur in °C 12 14 9 12 15 16 15 15 11 8 13 13 15 12
  2. Erstellen Sie eine Klasse Wetterstation, welche ein Attiribut temperaturen in Form einer Ganzzahlreihung hat.
    Der Konstruktor initialisiert die Reihung temperaturen in der oben angegebenen Form.
    Außerdem gibt es eine Operation ausgabe zum Ausgeben aller Werte in temperaturen.
    Testen Sie anschließend mit Hilfe einer Test-Klasse Ihre Implementierung.

    Entwickeln Sie Operation max (min), um die maximale (und minimale) Temperatur zu ermitteln und diese in geeigneter Form auszugeben.
    Testen Sie anschließend mit Hilfe Ihrer Test-Klasse Ihre Implementierung.

    Entwickeln Sie Operation max2 (min2), um die maximale (und minimale) Temperatur und den dazugehörigen Tag zu ermitteln und diese in Form einer Reihung der Länge 2 zurückzugeben.
    Testen Sie anschließend mit Hilfe Ihrer Test-Klasse Ihre Implementierung.

    Entwickeln Sie eine Operation avg, welche die Durchschnittstemperatur der zwei Wochen bestimmt und zurückgibt.
    Berechnen Sie den Durchschnittswert noch einmal selbst und vergleichen Sie die beiden Ergebnisse.
    Erläutern Sie, wie eventuelle Unterschiede zu Stande kommen und passen Sie Ihre Klasse Wetterstation geeignet an.

    Die Operation erweiterteInfos soll eine Reihung temperaturen, welche übergeben wird und eine unbekannte Anzahl von Tagestemperaturen beinhaltet, um drei Elemente erweitern, die vorhandenen Temperaturen übertragen, die Durchschnittstemperatur aller Tage an den ersten neuen Platz setzen. An die beiden anderen Plätze soll zuerst die Anzahl der Tage eingetragen werden, die mindestens 3° C unter der Durchschnittstemperatur und an die letzte Stelle die Anzahl der Tage, die mindestens 3° C über der Durchschnittstemperatur lagen. Die so entstandene Ganzzahlreihung soll zurückgegeben werden. Die Operation durchschnitt(int[] t), welche das arithmetische Mittel aller in t gespeicherten Zahlen als gerundete Ganzzahl bestimmt und zurückgibt, kann genutzt werden. Ein erster Entwurf der Operation erweiterteInfos ist im unten abgebildeten Struktogramm zu sehen.

    Klassenkarte der Klasse Feuerwerk

    Analysieren Sie die Funktionsweise des Algorithmus, wenn die Reihung 9, 13, 8, 10, 10, 5, 6 übergeben wird.
    Geben Sie die Reihung neu an, die am Schluss zurückgegeben wird.

    Verändern Sie das Struktogramm, sodass die Operation wie gewünscht funktioniert.

  1. (eA) Die folgenden Aufgaben beziehen sich auf das Adventure-Szenario. Wir hatten zuletzt den Magier hinzugefügt.

    Sie werden im Folgenden primär die Klassen Held, Goblin und Magier benötigen. Der letzten Stand wird Ihnen in der Online-IDE zur Verfügung gestellt oder Sie arbeiten mit Ihrer Version weiter.

  2. Ein Magier soll zusätzlich das Attribut zauberbuch in Form eines String-Array haben und alle Magier sollen mit den Zaubersprüchen Fliegen, Feuerball und Blitz starten. Das Zauberbuch muss also mindestens die Länge 3 haben.
    Erweitern Sie Ihren Implementierung geeignet und testen Sie diese.

    Implementieren Sie für die Klasse Magier eine Operation getZauber, welche alle Zauber des Zauberbuchs auf der Konsole ausgibt und testen Sie Ihre Operation.

    Ihr erstellter Magier soll anstelle des Zaubers Feuerball den Zauber Feuerblitz gelernt haben.
    Implementieren Sie eine Operation setZauber(int index, String zauber), um an einer bestimmten Stelle (index) einen neuen Zauber (zauber) einzutragen.

    Die Operation changeZauber(int i1, int i2) soll im Zauberbuch die beiden Zauber an den übergebenen Stellen tauschen.
    Implementieren Sie diese Operation.

    Der Magier hat nun eine Tasche voller Zaubertränke und jeder Zaubertrank wird nur durch einen ganzzahligen Wert (int) repräsentiert (s. Ausschnitt des Klassendiagramm neben den Aufgabenteilen), um den er heilt. Das Zauberbuch wurde bereits in der vorherigen Aufgabe umgesetzt.
    Ergänzen Sie zunächst das Attribut in Form einer Reihung zaubertraenke vom Inhaltstyp int und initialisieren Sie diese mit den Werten: {42, 7, 1, 17, 19}

    Implementieren Sie eine Operation minZaubertraenke, welche den Index des kleinsten Wertes ermittelt und zurückgibt. Machen Sie dieses nochmal für maxZaubertraenke.
    Implementieren Sie eine Operation ordneZaubertraenke(), welche den kleinsten Wert der Reihung an den Index 0 tauscht.

    Es soll eine neue Klasse Zauber erstellt werden. Ein Zauber verstärkt durch das Attribut staerke den Schaden beim Zaubern.
    Implementieren Sie diese Anforderung und passen Sie gegebenenfalls die Operationen und Attribute der Klasse Magier an.

Die Modellierung unserer Raketenreihung wird nun erweitert, sodass auch mehrere Reihen möglich sind (siehe Abbildung unten).
Wir gehen davon aus, dass in jeder Zeile gleich viele Raketen platziert werden können. Eine Beispielbelegung würde wie folgt aussehen:

0 1 2 3
0 Rot Grün Grün Blau
1 Rot Grün Grün Blau
2 Rot Grün Grün Blau
3 Rot Grün Grün Blau
4 Rot Grün Grün Blau
5 Rot Grün Grün Blau

Deklarieren einer zweidimensionalen Reihung
Wenn wir die oben abgebildete zweidimensionale Reihung deklarieren wollen, dann wird das in Java so gemacht:

Deklarieren Erklärung
String[][] test; String [ ][ ] test ;
Datentyp der Objekte in der Reihung Klammern Name des Arrays Ende der Anweisung

Initialisieren einer zweidimensionalen Reihung
Bisher wurde nur Speicherplatz für die 2d-Reihung test reserviert, in welche nur Zeichenketten gespeichert werden dürfen. Damit der Computer weiß, wie groß die 2d-Reihung sein soll, muss man sie initialisieren:

Initialisieren Erklärung
test=new String[6][4]; test = new String[6][4]
Name der Reihung Wertzuweisung Konstruktor einer Reihung und Angabe der Größe --> Zeilen zuerst, Spalten später

Jetzt existiert eine leere 2d-Reihung mit 6 Zeilen und 4 Spalten, in welcher Zeichenketten gespeichert werden dürfen. Aktuell ist die 2d-Reihung aber leer. Das bedeutet, dass an allen Positionen null eingetragen ist.

0 1 2 3
0 null null null null
1 null null null null
2 null null null null
3 null null null null
4 null null null null
5 null null null null

Man kann auch beide Schritte gleichzeitig machen, wenn die Größe der 2d-Reihung direkt angegeben werden soll:

Deklarieren + Initialisieren Erklärung
String[][] test=new String[6][4]; String[][] test = new String[6][4]
Deklarieren Wertzuweisung Initialisieren

Werte in einer 2d-Reihung speichern
Nun sollen die Raketen aus dem Einstiegsbeispiel in unserer 2d-Reihung gespeichert werden. Wir starten mit der ersten Rakete, "Rot" am Index (0,0) und einer grünen woanders:

Speichern Erklärung
test[0][0] = "Rot"; test [0][0] = "Rot"
Name der Reihung Index, der angesprochen werden soll --> Zeile 0 und Spalte 0 Wertzuweisung Neuer Wert, hier ein String

Speichern Erklärung
test[3][1] = "Grün"; test [3][1] = "Grün"
Name der Reihung Index, der angesprochen werden soll --> Zeile 3 und Spalte 1 Wertzuweisung Neuer Wert, hier ein String

Man kann auch direkt Deklarieren, Initialisieren und Werte zuweisen, sollte man dies wollen bzw. können. Wir machen das, und auch die folgenden Beispiele, für eine kleinere 2d-Reihung als die im Einstiegsbeispiel:

0 1
0 Rot Grün
1 Blau Rot
2 Weiß Rot
3 Rot Weiß

Alles zusammen Erklärung
String[][] test = {{"Rot","Grün"}, {"Blau","Rot"},{"Weiß","Rot"}, {"Rot","Weiß"}}; String[][] test = {{"Rot","Grün"}, {"Blau","Rot"},{"Weiß","Rot"}, {"Rot","Weiß"}};
Deklarieren Wertzuweisung Werte direkt speichern

Werte aus 2d-Reihung auslesen
Wenn eine 2d-Reihung vorhanden ist und man sich für deren Inhalte interessiert, kann man auch die gespeicherten Objekte abfragen. Man kann so entweder Einträge der 2d-Reihung in anderen Variablen speichern oder sich einfach einen Wert ausgeben lassen. Mit einer Schleife kann man sich auch alle Elemente der Reihung ausgeben. Mit einer geschachtelten Schleife kann man sich auch alle Elemente der Reihung ausgeben:

Ein Wert aus test in einer Variablen speichern Erklärung
String s;
s = test[1][0];
s = test[1][0]
Wo gespeichert wird Der Wert am Index (1,0), hier "Blau", wird zurückgegeben

Einen Wert aus test ausgeben Erklärung
print(test[0][1]); print(...) test[0][1]
Etwas auf der Konsole anzeigen Der Wert am Index (0,1), hier "Grün", wird zurückgegeben

Alle Werte in test ausgeben Erklärung
for(int i=0; i<4; i++) {
for(int j = 0; j<2;j++){
print(test[i][j]);
}
}
for(int i=0; i<4; i++) for(int j = 0; j<2;j++) print(test[i][j])
Äußere Schleife für die Zeilen Innere Schleife für die Spalten Der Wert am aktuellen Index (i,j), wird zurückgegeben

Länge der Reihung angeben
Man kann sich auch die Anzahl der Zeilen und Spalten einer 2d-Reihung zurückgeben lassen:

Zeilenanzahl von test nutzen Erklärung
int a = test.length; int a = test.length
Ganzzahlvariable a deklarieren und einen Wert zuweisen Länge von test, hier 4, wird zurückgegeben

Spaltenanzahl von test nutzen Erklärung
int a = test[0].length; int a = test[0].length
Ganzzahlvariable a deklarieren und einen Wert zuweisen Länge von test, hier 4, wird zurückgegeben

Dies kann man nutzen, um die gesamte 2d-Reihung wie weiter oben zu durchlaufen, wenn deren Länge nicht bekannt ist.

Alle Werte in test ausgeben Erklärung
for(int i=0; i<test.length; i++) {
for(int j = 0; j<test[0].length;j++){
print(test[i][j]);
}
}
for(int i=0; i<test.length; i++) for(int j = 0; j<test[0].length;j++) print(test[i][j])
Äußere Schleife für die Zeilen Innere Schleife für die Spalten Der Wert am aktuellen Index (i,j), wird zurückgegeben

Achtung: Wenn ein negativer Index oder ein Index, der größer ist, als das letzte Element der 2d-Reihung, eingegeben wird, dann wird eine IndexOutOfBoundsException ausgelöst, die angibt, dass man den Index-Bereich/die Größe der Reihung verlassen hat.

  1. Um zunächst ein wenig den Umgang mit 2d-Reihungen kennenzulernen sind oben die wichtigsten Informationen zu 2d-Reihungen in Java an konkreten Beispielen vorgegeben. Nutzen Sie schrittweise die Erklärungen (oben), um die folgenden Aufgaben zu bearbeiten.
  2. Deklarieren und initialisieren Sie auf je zwei verschiedene Weisen die folgenden Reihungen in einer Klasse Array2DTest in Java.

    • eine 2d- Zeichenkettenreihung mit den Zeilen:
      • haus, auto, baum
      • pflanze, katze, maus
    • eine Ganzzahlreihung mit den Zeilen:
      • 4 , 32
      • -1, 79
      • 13, 0
    • eine char-Reihung mit den Zeilen:
      • a, b, c
      • d, e, f
      • g, h, i

    Ersetzen Sie in jeder Ihrer Reihungen einen beliebigen Wert durch einen anderen.

    Lassen Sie sich nacheinander die Werte einer der drei Reihungen vollständig ausgeben und zwar in mindestens drei Varianten:

    • In einer einzigen Zeile,
    • Jede Zeil der Reihung ist auch eine eigene Zeile auf der Konsole,
    • Es wird nur jeder zweite Wert ausgeben.

  1. Die Klasse Feuerwerk soll zunächst wie rechts abgebildet umgesetzt werden.
    Das Attribut anlage ist eine zweidimensionale Reihung vom Inhaltstyp Zeichenkette, welche die Raketen in Form von Zeichenketten (die jeweilige Farbe) speichern soll. Das Attribut dim ist eine Reihung vom Inhaltstyp Ganzzahl und der Größe 2, in der an der ersten Stelle die Zeilen- und an der zweiten die Spaltenanzahl der Anlage steht.

    Klassenkarte der Klasse Feuerwerk

    Entscheiden Sie, ob Sie die Basis- oder die Challenge-Variante umsetzen wollen. Sie können auch jederzeit zwischen diesen wechseln.

  2. Ihnen wurde über die Online IDE ein Framework Feuerwerk2Dim zur Verfügung gestellt.

    Führen Sie die Klasse FeuerwerkTest aus.
    Erläutern Sie die Funktionsweise der Operationen public Feuerwerk(String[][] s), public int[] getDim() und der Anweisungen in der Klasse FeuerwerkTest.

    Die weiteren in der Klassenkarte abgebildeten Operationen haben die folgende Funktionsweise:

    • getRakete soll die an der übergebenen Zeile a und übergebenen Spalte b gespeicherte Rakete zurückgeben.
    • showAnlage soll die gesamte Anlage auf der Konsole ausgeben.
    • setRakete soll die Rakete an der übergebenen Zeile a und übergebenen Spalte b mit der Zeichenkette r überschreiben.

    Schauen Sie sich die noch nicht gefüllten Operationen in der Klasse Feuerwerk an und erläutern Sie die einzelnen Elemente der Methodenköpfe. Gehen Sie hierbei insbesondere auf Übergabeparameter und Rückgabewerte ein.

    Vervollständigen Sie die Operationen getRakete, showAnlage und setRakete.
    Testen Sie die get-Operationen und showAnlage.
    Ersetzen Sie die blaue Rakete durch eine weiße Rakete.

    Eine weitere get-Operation soll ergänzt werden. Die Operation getRakete(String c) erhält eine Farbe als Zeichenkette übergeben und soll die erste Stelle zurückgeben, an dem sich eine Rakete der gesuchten Farbe befindet.
    Implementieren Sie die neue Operation getRakete.

    Die Operation hatFarbe soll überprüfen, ob eine übergebene Farbe in der Reihung anlage enthalten ist und in diesem Fall true zurückgeben, anderenfalls false.
    Implementieren Sie die Operation hatFarbe.

    Die Operation changeRakete(int i1, int i2, int i3, int i4) soll in der Reihung anlage die Raketen an den beiden übergebenen Positionen (i1,i2) und (i3,i4) tauschen.
    Implementieren Sie die Operation changeRakete.

    Nun soll es die Möglichkeit geben, die Flughöhe der Raketen der Reihung anlage zu speichern. Hierfür erhält die Klasse Feuerwerk ein zweites Attribut hoehe in Form einer zweidimensionalen Reihung vom Inhaltstyp Ganzzahl.
    Ergänzen Sie Ihre Klasse Feuerwerk um das Attribut hoehe. Verändern Sie den bisherigen Konstruktor so, dass er die Werte in hoehe auf 0 setzt.
    Implementieren Sie einen zweiten Konstruktor, dem zusätzlich zur Zeichenkettenreihung eine zweidimensionalen Reihung vom Inhaltstyp Ganzzahl übergeben wird, welche unter dem Attribut hoehe gespeichert werden soll.
    Erstellen Sie nun ein Objekt der Klasse Feuerwerk mit den Raketen wie im Einstieg des Abschnitts und zufälligen Flughöhen zwischen mindestens 50m und maximal 120m.

    Implementieren Sie eine Operation minHoehe, welche das Indexpaar des kleinsten Wertes der Flughöhe ermittelt und zurückgibt. Machen Sie dieses nochmal für maxHoehe.
    Implementieren Sie eine Operation ordneHöhe, welche den kleinsten Wert der Flughöhe an den Index (0,0) tauscht.

    Optimieren Sie Ihre Operationen, sodass nicht sinnvolle Eingaben abgefangen werden.
    Beispielsweise könnte man bei getRakete(int a, int b) einen Index eingeben, den es nicht gibt.

    Die Klasse Feuerwerk soll zunächst wie oben abgebildet umgesetzt werden.
    Das Attribut anlage ist eine zweidimensionale Reihung vom Inhaltstyp Zeichenkette, welche die Raketen in Form von Zeichenketten (die jeweilige Farbe) speichern soll. Das Attribut dim ist eine Reihung vom Inhaltstyp Ganzzahl und der Größe 2, in der an der ersten Stelle die Zeilen- und an der zweiten die Spaltenanzahl der Anlage steht.
    Dem Konstruktor wird eine zweidimensionale Reihung vom Inhaltstyp Zeichenkette übergeben, welche als anlage gespeichert werden soll. Beim Aufrufen des Konstruktors sollen die Zeilen- und Spaltenanzahl der übergebenen Reihung bestimmt und in das Attribut dim gespeichert werden. Die get-Operation sollen die Dimension der Anlage (dim) und eine Rakete an einer bestimmten Stelle zurückgeben. Die Operation showAnlage soll die gesamte Anlage auf der Konsole ausgeben. Die set-Operation soll an den übergebenen Index die übergebene Rakete platzieren.

    Implementieren Sie die Klasse Feuerwerk wie oben beschrieben.

    Erstellen Sie eine Testklasse, in welcher Sie ein Objekt der Klasse Feuerwerk mit den Raketen so belgen, wie zu Beginn dieses Abschnittes dargestellt.
    Testen Sie die get-Operationen und showAnlage.
    Ersetzen Sie die blaue Rakete durch eine weiße Rakete.

    Eine weitere get-Operation soll ergänzt werden. Die Operation getRakete(String c) erhält eine Farbe als Zeichenkette übergeben und soll die erste Stelle zurückgeben, an dem sich eine Rakete der gesuchten Farbe befindet.
    Implementieren Sie die neue Operation getRakete.

    Die Operation hatFarbe soll überprüfen, ob eine übergebene Farbe in der Reihung anlage enthalten ist und in diesem Fall true zurückgeben, anderenfalls false.
    Implementieren Sie die Operation hatFarbe.

    Die Operation changeRakete(int i1, int i2, int i3, int i4) soll in der Reihung anlage die Raketen an den beiden übergebenen Positionen (i1,i2) und (i3,i4) tauschen.
    Implementieren Sie die Operation changeRakete.

    Nun soll es die Möglichkeit geben, die Flughöhe der Raketen der Reihung anlage zu speichern. Hierfür erhält die Klasse Feuerwerk eine zweites Attribut hoehe in Form einer zweidimensionalen Reihung vom Inhaltstyp Ganzzahl mit den Dimensionen der Reihung anlage.
    Verändern Sie den bisherigen Konstruktor so, dass er die Werte in hoehe auf 0 setzt.
    Implementieren Sie einen zweiten Konstruktor, dem zusätzlich zur Zeichenkettenreihung eine zweidimensionale Ganzzahlreihung übergeben wird, welche unter dem Attribut hoehe gespeichert werden soll.
    Erstellen Sie nun ein Objekt der Klasse Feuerwerk mit den Raketen wie im Einstieg des Abschnitts und zufälligen Flughöhen zwischen mindestens 50m und maximal 120m.

    Implementieren Sie eine Operation minHoehe, welche das Indexpaar des kleinsten Wertes der Flughöhe ermittelt und zurückgibt. Machen Sie dieses nochmal für maxHoehe.
    Implementieren Sie eine Operation ordneHöhe, welche den kleinsten Wert der Flughöhe an den Index (0,0) tauscht.

  1. Vergleichen Sie in einer eigenen Testklasse die beiden folgenden Algorithmen:
    int[] zahlen = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    for (int i = 0; i < zahlen.length; i++) {
        for (int j = 0; j < zahlen[0].length; j++) {
            print(zahlen[i][j] + " ");
        }
        println();
    }
    
    
    int[] zahlen = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    for (int[] zeile: zahlen) {
        for (int element : zeilen) {
            print(element + " ");
        }
        println();
    }
    
    
    Die linke Variante iteriert mit zwei geschachtelten for-Schleifen über die zweidimensionale Reihung zahlen.
    Die rechte Variante nutzt zwei geschachtelte for-each-Schleifen. Diese kann man alternativ zu for- Schleifen auch für zweidimensionale Arrays nutzen.
  1. Ein Objekt der Klasse Color ist eine Farbe in RGB-Codierung. Hierbei werden die Intensitäten der Grundfarben Rot, Grün und Blau in jeweils ganzzahligen Werten im Bereich von 0 bis 255 angegeben. Beispiele für konkrete Farben finden Sie unten.

    Farbe HTML-Name RGB-Dezimalcodierung
    Weiß (255,255,255)
    Schwarz (0,0,0)
    Rot (255,0,0)
    Limette (0,255,0)
    Blau (0,0,255)
    Gelb (255,255,0)
    Cyan (0,255,255)
    Magenta (255,0,255)
    Grün (0,128,0)
    Lila (128,0,128)
    Beispiele für die RGB-Codierung von Farben

    Die Klasse Feuerwerk wird um die Klasse Color und andere Funktionalitäten erweitert, siehe unten. Der Datentyp der Reihung anlage wird dadurch von Zeichenkette zu Color. In einer Challenge-Variante wird zusätzlich noch eine Klasse Rakete genutzt, wodurch der Datentyp der Reihung anlage von Zeichenkette zu Rakete wird.
    Entscheiden Sie sich zu Beginn dieser Aufgabe für eine der beiden Varianten. Die Klasse Color liegt bereits vor und kann entspechend genutzt werden.

  2. Klassenkarte der Klassen Feuerwerk und Color

    Verändern Sie Ihre bisherige Klasse Feuerwerk entsprechend der neuen Situation.

    Ist ein Platz der Reihung anlage nicht besetzt, wird eine Rakete mit der Farbe Schwarz mit Flughöhe 0 an die freie Stelle gesetzt.
    Verändern Sie die Konstruktoren entsprechend.
    Erstellen Sie ein neues Objekt der Klasse Feuerwerk und belegen Sie die Reihung anlage so, dass in der ersten Zeile rote Raketen, in der zweiten blaue, in der dritten alle Plätze frei, in der vierten grüne und in der fünften Zeile blaue Raketen platziert sind. Es soll insgesamt vier Spalten geben.

    Die Operation setRakete soll nun eine Rakete an einen bestimmten Platz setzen, wenn dieser Platz nicht besetzt ist.
    Verändern Sie die Operation setRakete nach diesen Vorgaben.
    Testen Sie die Operation, indem Sie eine weiße Rakete an die freie Stelle (schwarz) setzen.

    Die Operation zuenden der Klasse Feuerwerk erhält eine Reihung aus Farben als Übergabeparameter und soll zählen, wie viele Raketen der jeweiligen Farben in der Reihung anlage enthalten sind und deren Anzahlen in einer Ganzzahlreihung zurückgeben.
    Implementieren Sie die Operation zuenden.
    Testen Sie die Operation für rote und blaue Raketen.

    Klassenkarte der Klassen Feuerwerk, Rakete und Color

    Implementieren Sie die Klasse Rakete und nutzen Sie die Klasse Color entsprechend.

    Verändern Sie Ihre bisherige Klasse Feuerwerk entsprechend der neuen Situation.

    Ist ein Platz der Reihung anlage nicht besetzt, wird eine Rakete mit der Farbe Schwarz und 0.0 bzw. 0 für die anderen Attributswerte an die freie Stelle gesetzt.
    Verändern Sie den Konstruktor entsprechend.
    Erstellen Sie ein neues Objekt der Klasse Feuerwerk und belegen Sie die die Reihung anlage mit Raketen der Farben Rot, Blau, freier Platz, Grün, Blau, in der angegebenen Reihenfolge. Es oll insgesamt vier Spalten geben.
    Das Attribut laenge soll einen beliebigen Wert zwischen 10.0 und 25.0 haben, zdauer zwischen 1.0 und 3.0 und hoehe zwischen 20 und 60.

    Die Operation setRakete soll nun ein Objekt der Klasse Rakete an einen bestimmten Platz setzen, wenn dieser Platz nicht besetzt ist. Ist der Platz jedoch besetzt, soll die neue Rakete an den darauf folgenden Platz gesetzt und die restlichen Plätze nach hinten geschoben werden. Gegebenenfalls muss die letzte Rakete enfernt werden.
    Verändern Sie die Operation setRakete nach diesen Vorgaben.
    Testen Sie die Operation, indem Sie eine weiße Rakete an eine freie und eine besetzte Stelle setzen.

    Die Operation zuenden der Klasse Feuerwerk erhält eine Reihung aus Farben und eine Ganzzahl als Übergabeparameter. Sie soll zählen, wie viele Raketen der jeweiligen Farben in der Reihung anlage enthalten sind und gleichzeitig mindestens die übergebene Flughöhe erreichen und deren Anzahlen in einer Ganzzahlreihung zurückgeben.
    Implementieren Sie die Operation zuenden.
    Testen Sie die Operation für rote und blaue Raketen und einer Flughöhe von 40.

  1. (eA) Im vorherigen Abschnitt wurde die Klasse Magier verändert. Das neue UML-Klassendiagramm sieht nun wie folgt aus: Die vier Klassen werden Ihnen von Ihrer Lehrkraft in der Online-IDE freigegeben oder Sie verwenden Ihre eigenen.
    Der Magier hat auf einer Landkarte verzeichnet, wie magisch manche Orte sind. Ein geringer Wert spiegelt eine geringe Magie wieder und ein hoher Wert entspricht, dass der Ort sehr magisch ist. So sieht dann seine Übersicht am Ende, wie unten abgebildet aus.
    1 2 3 4 5 6 7 8 9
    A 2 7 4 6 7 2 1 1 1
    B 8 9 8 8 5 4 1 0 1
    C 2 8 7 1 1 1 1 1 1
    D 6 7 4 8 2 2 2 2 2
    E 6 4 7 2 2 1 1 5 5
    F 4 5 3 8 8 1 1 5 4
    G 1 1 2 9 7 2 3 4 7
  2. Implementieren Sie in der Klasse Magier das Attribut magischeOrte als zweidimensionale Reihung vom Inhaltstyp Ganzzahl und lassen Sie im Konstruktor die zweidimensionale Reihung mit zufälligen Ganzzahlwerten größer oder gleich 0 und kleiner oder gleich 9 initialisieren. Hinweis: Die Angabe A bis G und 1 bis 9 sind nur zur Orientierung und gehören nicht dazu.

    Implementieren Sie als nächste die Operation ausgabeMagischeOrte, mit welcher die Stärke der magischen Orte auf der Konsole angezeigt werden.

    Nun hat sich die magische Stärke mancher Orte geändert. So ist bspw. die Stärke bei B8 von 0 auf 2 gestiegen, dafür ist die Stärke bei B2 von 9 auf 7 gesunken.
    Implementieren Sie die Operation setMagischeOrte(int i1, int i2, int neuerWert), welche die Position der Änderung erhält (i1 ist die Zeile und i2 die Spalte) und anschließend den Wert an dieser Stelle ändert (neuerWert).

    Implementieren Sie die Operation getMagischeOrte(int i1, int i2), welche den Wert an der entsprechenden Position zurückgibt.

    Nun soll ermittelt werden, wie magisch diese abgebildete Region im Durchschnitt ist.
    Implementieren Sie die Operation durchschnittMagischeOrte, welche die durchschnittliche magische Stärke der Region ermittelt und anschließend zurückgibt.

    Als nächstes will der Magier wissen, wo der stärkste magische Ort in seiner Region ist.
    Implementieren Sie eine geeignete Operation maxMagischeOrte, welche die Indices des entsprechenden Ortes zurückgibt.
    Setzen Sie dieses ebenfalls für den Ort mit der geringsten magischen Stärke um.

    Die Reihung magischeOrte ist 7 Zeilen und 9 Spalten groß. Sie soll auf 10x10 vergrößert werden, die bisherigen Werte an den alten Stellen behalten und an allen neuen Stellen mit dem Wert 0 befüllt werden.
    Entwickeln Sie eine geeignete Lösungsstrategie für dieses Problem.

  1. Wir kommen zurück zum Feuerwerk, wie in Aufgabe 4.
    Klassenkarte der Klassen Feuerwerk und Color
    Einer der angestellten Pyrotechniker befürchtet, in die gerade zusammengestellte Anlage ungeeignete Raketen verbaut zu haben.
    Um möglichst viele Effekte zu erzielen, dürfen maximal zwei weitere Raketen im direkten Umfeld einer Rakete, die gleiche Farbe haben. Betrachten wir z. B. die Rakete r5 an irgendeiner Stelle der Anlage, dann dürfen in der unteren Abbildung maximal zwei andere Raketen r1 bis r9 die gleiche Farbe wie die Rakete r5 haben.
    Gültige Belegung
    r1 r2 r3
    r4 r5 r6
    r7 r8 r9
    Ungültige Belegung
    r1 r2 r3
    r4 r5 r6
    r7 r8 r9
    Ungültige Belegung
    r1 r2 r3
    r4 r5 r6
    r7 r8 r9
  2. Um das Problem zu lösen, entwirft der Pyrotechniker, der ebenfalls Programmierer ist, einen Algorithmus in Form eines Struktogramms, siehe weiter unten.
    Die Reihung anlage steht, wie oben mit den Raketen r1 bis r9 dargestellt, als globale Variable zur Verfügung, wobei die Raketen die folgenden Attributwerte für (red, green, blue) haben: r1: (255,0,0), r2: (255,0,0), r3: (255,255,0), r4: (255,0,255), r5: (255,0,0), r6: (255,128,0), r7: (255,128,0), r8: (255,128,0), r9: (255,128,0).

    Belegung
    r1 r2 r3
    r4 r5 r6
    r7 r8 r9

    Weiterhin gibt die Operation pruefeFeld(int x, int y) wahr zurück, wenn der Index (x,y) im Bereich der zweidimensionalen Reihung anlage liegt, sonst falsch.
    Analysieren Sie die Funktionsweise des Algorithmus, indem Sie die Trace-Tabelle weiter unten ergänzen.
    Als Übergabe betrachten wir die Koordinaten von r2. Die erste Zeile ist der Zustand vorm ersten Durchlauf von "wiederhole für yPos von -1 bis 1".

    Struktogramm
    Tracetabelle mit Startbelegung
    y x yPos xPos anzahl aktuelle Rakete
    0 1 -1 -1 0 -

    Verändern Sie das Struktogramm, sodass der Algorithmus wie gewünscht arbeitet.

    Prinzipiell kann man den Algorithmus auch so anlegen, dass er die gesamte Reihung anlage als Übergabeparameter erhält und diese durchläuft. Die Rückgabe soll eine zweidimensionale Reihung sein, in der an jedem Index die Anzahl der im Umfeld vorhanden identischen Raketen für die jeweilige Rakete steht. Was dann so aussieht:

    Übergabe
    r1 r2 r3
    r4 r5 r6
    r7 r8 r9
    Ablauf
    Rückgabe
    2 2 0
    0 2 2
    1 3 2

    Erweitern Sie das Struktogramm entsprechend.

Der Kontext aus dem Abschnitt und das Material mit den Filmdosen sind nach einer Idee von Laura Hembrock (Hörstel, 2023) erstellt worden.
Der Leiter eines Baumarktes hat für seinen Markt in eine moderne Pack- und Sortieranlage investiert, welche die Regale automatisch mit Waren befüllen kann. Hierfür gibt es kleine Roboter, welche Teile aus dem Lager zu den entsprechenden Regalen fahren können. Weiterhin gibt es in jedem Warengang Roboterarme, welche mit Hilfe einer Kamera Objekte erkennen, mit einem Greifarm Objekte aufnehmen und diese mit einem eingebautem Kraftsensors wiegen können.
Bei einem Wasserrohrbruch wurden unter anderem die unteren Regale im Metallwarengang so geflutet, dass viele Behälter mit verschiedenen kleinen Schrauben in den Gang gespült und so beschädigt wurden, dass ihre Etiketten abgelöst wurden. Die Schraubenbehälter haben alle die gleichen Maße aber unterscheiden sich in ihrem Gewicht voneinander. Der Roboterarm kann nun nicht mehr durch mit seiner Kamera entscheiden, in welches Fach die jeweiligen Schraubenbehälter gehören. Glücklicherweise gibt es noch den Kraftsensor. Mit diesem kann der Roboterarm eine gegebene Reihe von Behältern sortieren.
Dafür haben gibt es zwei Möglichkeiten.

Sortierroboter
Aufsteigend sortieren
12,1 g 50,8 g 62,0 g 80,4 g 105,0 g
Jeder Behälter ist nicht schwerer, als alle anderen Behälter rechts davon.
Absteigend sortieren
105,0 g 80,4 g 62,0 g 50,8 g 12,1 g
Jeder Behälter ist nicht schwerer, als alle anderen Behälter links davon.

Unsere Behälter sollen aufsteigend sortiert werden. Der leichteste Behälter soll also ganz links sein und danach werden alle anderen Behälter schwerer. Der Einfachheit wegen gehen wir davon aus, dass es keine zwei Behälter mit gleichem Gewicht gibt. Die Behälter kann man sich als Reihung mit Objekten, den Behältern, vorstellen.

Behälter ? g ? g ? g ? g ? g ? g ? g ? g ? g
Index 0 1 2 3 4 5 6 7 8

Man sieht natürlich nicht sofort, welcher Behälter der leichteste ist und somit nach ganz links gehört.
Hierfür muss sich der Roboterarm diesen Behälter nehmen, sein Gewicht messen, dieses Gewicht speichern und dann mit allen anderen Behältern vergleichen.
Irgendwann und irgendwie, die obige Vorgehensweise ist schon ein Teil des Verfahrens, hat der Roboterarm einen Behälter als Leichtesten identifiziert und kann diesen nach ganz links setzen.
Man merkt schon, dass hierfür eine konkrete und kleinschrittige Abfolge von Handlungsanweisungen mit entsprechenden Kontrollstrukturen notwendig ist, also ein Algorithmus benötigt wird! Es wird Zeit sich noch einmal mit einer Definition eines Algorithmus auseinander zu setzen.

Definition Algorithmus

Unter einem Algorithmus versteht man eine Verarbeitungsvorschrift, die von einem mechanisch oder elektronisch arbeitenden Gerät bzw. auch von einem Menschen durchgeführt werden kann. Aus der Präzision der sprachlichen Darstellung eines Algorithmus muss die Abfolge der einzelnen Verarbeitungsschritte eindeutig hervorgehen. Wenn Wahlmöglichkeiten vorhanden sind, so muss dann genau festgelegt werden, wie die Auswahl einer Möglichkeit erfolgen soll.

Quelle: Grundlagen der Informatik, S. 31

Die Überlegungen zum eigentlichen Sortieren müssen also kleinschrittig entwickelt werden, sodass am Ende ein Sortieralgorithmus daraus abgeleitet werden kann. Dafür muss zunächst einmal geklärt werden, welche Schritte erlaubt sind bzw. während des Sortierens durchführt werden können. Unter anderem sind diese Schritte notwendig:

  1. Das Speichern eines Gewichts in einer Variablen,
  2. Das Speichern einer bestimmten Stelle an der ein Behälter ist,
  3. Der Vergleich des Gewichts eines Behälters mit dem eines anderen,
  4. Einen Behälter in der Reihung mit einem anderen Behälter tauschen,
  5. Die Reihung mit Wiederholungen durchlaufen.

  1. Im Folgenden soll es darum gehen ein algorithmisches Vorgehen des Sortieren zu entwickeln. Gehen Sie dabei in Partnerarbeit vor.
  2. Holen Sie sich einen Satz Schraubenbehälter (Filmdosen) und einen Bogen Papier mit vorgegebenen Stellplätzen. Positionieren Sie die Filmdosen in einer beliebigen Reihenfolge auf den Plätzen.

    Entwickeln Sie eine Möglichkeit, die Behälter möglichst systematisch zu sortieren. In diesem Szenario sind Sie der Computer, die Filmdosen die Schraubenbehälter und die Stellplätze auf dem Papier sind die Reihung.
    Achtung: Wenn Sie sich etwas merken wollen, notieren Sie sich dies auf einen weiteren Zettel bzw. digital.

    Entwickeln Sie aus Ihrem Sortierverfahren eine deutlich kleinschrittigere Variante, die einem Algorithmus nahe kommt. Hierfür können Sie unter anderem die fünf oben genannten Anweisungen nutzen.
    Achtung: Einige für Sie kleinen Schritte bedeuten für eine Maschine eine detailliertere Handlungsanweisung und sie muss sich oft auch Informationen merken. Ein konkretes Beispiel ist unter anderem das Tauschen eines Behälters mit einem anderen.

    Stellen Sie sich Ihre Sortierverfahren vor und diskutieren Sie mögliche Vor- und Nachteile der Verfahren.

  1. Gehen wir also davon aus, dass die Behälter in einer Reihung b vorliegen und jedes Objekt der Klasse Behaelter, welche in der Reihung gespeichert sind, eine Operation getWeight hat, welche das Gewichte des jeweiligen Behälters als double zurückgibt.
    Erläutern Sie die Funktionsweise der in Wortform (Pseudocode) gegebenen Operation ???.
    ??? (Behaelter[] b)
    setze Wahrheitswert s auf wahr
    für i =0 bis Länge von b-2
        für j=i+1 bis Länge von b-1
            wenn b[i].getWeight > b[j].getWeight
                setze s auf falsch
    gib s zurück
    
    
  1. Bearbeiten Sie die Gruppenaufgaben, die Ihnen zugewiesen wurden.
  2. Man stellt sich hier vor, dass man immer nur den nächsten Behälter, angefangen mit dem ersten von links, sieht und diesen in den Teil der Reihung links ab diesem Behälter an die richtige Stelle einordnet. Konkret:
    Im ersten Schritt hat man noch keinen der Behälter sortiert. Dann nimmt man sich den ersten Behälter, der noch nicht einsortierten Reihung und ordnet diesen an die richtige Stelle (ja, im ersten Schritt ist es auch die erste Stelle und der Behälter bleibt dort!) ein.
    Nun hat man eine bereits sortierte, linke Teilreihung (Im ersten Schritt ist es nur die Teilreihung mit dem Index 0, die aus einem Element besteht) und kann den nächsten Behälter (Jetzt der zweite, betrachten und wiegen). Diesen vergleicht man nun mit allen bereits in der sortierten Teilreihung vorhanden Behältern, beginnend von rechts.
    Hat man einen Behälter gefunden, der leichter als der aktuell einzusortierende ist, kommt der neue Behälter rechts von diesem leichteren in die bereits sortierte Reihung, wobei die restlichen Behälter der sortierten Teilreihung eine Stelle weiter rücken müssen. Dies wiederholt man, bis alle Behälter betrachtet wurden.

    Nehmen Sie Ihre Filmdosen und das Blatt Papier mit der Reihung zu Hilfe, um sich den gesamten Ablauf zu verdeutlichen und führen Sie die Sortierung einmal durch.

    Formulieren Sie für das Verfahren Insertionsort einen Algorithmus in Form eines Pseudocodes, der die einzelnen Abläufe darstellt. Es kann davon ausgegangen werden, dass die zu sortierenden Behälter in einer Behaelter-Reihung b vorliegen.
    Nutzen Sie hierfür Begriffe wie:

    • Merke dir das Element/Speichere das Element x in einer Variable
    • Beginne am Anfang der bereits sortierten Reihung/beginne am Ende der bereits sortierten Reihung
    • Vergleiche Element x mit Element y
    • Wenn Bedingung a erfüllt ist, dann führe Anweisung b aus, sonst führe Anweisung c aus.
    • ...

    Zunächst ermittelt man den leichtesten Behälter aus der noch zu sortierenden Reihung und tauscht ihn mit dem Behälter, der momentan an der ersten Stelle der gesamten Behälterreihung steht.
    Danach bestimmt man aus der noch zu sortierenden Behälterreihung (im zweiten Schritt also alles hinter dem ersten Behälter) den nun leichtesten Behälter und tauscht diesen mit dem Behälter, der momentan an der zweiten Stelle der gesamten Behälterfolge steht usw.

    Nehmen Sie Ihre Filmdosen und das Blatt Papier mit der Reihung zu Hilfe, um sich den gesamten Ablauf zu verdeutlichen und führen Sie die Sortierung einmal durch.

    Formulieren Sie für das Verfahren Selectionsort einen Algorithmus in Form eines Pseudocodes, der die einzelnen Abläufe darstellt. Es kann davon ausgegangen werden, dass die zu sortierenden Behälter in einer Behaelter-Reihung b vorliegen.
    Nutzen Sie hierfür Begriffe wie:

    • Merke dir das Element/Speichere das Element x in einer Variable
    • Beginne am Anfang der bereits sortierten Reihung/beginne am Ende der bereits sortierten Reihung
    • Vergleiche Element x mit Element y
    • Wenn Bedingung a erfüllt ist, dann führe Anweisung b aus, sonst führe Anweisung c aus.
    • ...

  1. Eine mögliche Dokumentation zum Sortieren von Elementen ist:
    Dokumentation eines Sortierverfahrens, entnommen von Wikipedia
  2. Wenden Sie auf die untenstehende Kartenfolge die Sortieralgorithmen Insertion- und Selectionsortan.
    Dokumentieren Sie schrittweise, was in jedem Durchlauf der Algorithmen passiert und wie sich die Kartenfolge ändert.

    Kartenfolge

    Unten ist die schrittweise Sortierung einer Zahlenfolge angegeben.
    Erläutern Sie, nach welchem Verfahren die Sortierung vorgenommen und ob dieses korrekt ausgeführt wurde.
    Beschreiben Sie den Ablauf der einzelnen Sortierschritte und korrigieren Sie gegebenenfalls an den entsprechenden Stellen.
    4 2 1 6 3 5
    1 2 4 6 3 5
    1 2 3 6 4 5
    1 2 3 4 6 5
    1 2 3 4 5 6

    Sortieren Sie die folgende Zahlenfolge unter Verwendung aller Ihnen bekannter Sortieralgorithmen.
    Heben Sie den Tauschprozess farblich hervor, sowie etwaige bereits sortierte Teilfolgen, die bereits sortiert sind. Jeder Sortiervorgang soll in einer neuen Zeile geschrieben werden. Die zu sortierende Zahlenfolge ist: 7 5 3 1 8

  1. Beschreiben Sie den als Struktogramm dargestellten Algorithmus. Hierfür sollten Sie eine kleine Zahlenfolge wählen und den Algorithmus im Struktogramm anhand dieser Zahlenfolge durchgehen.
  2. Dokumentation eines Sortierverfahrens, entnommen von Wikipedia
  1. Erstellen Sie für die folgende Ablaufbeschreibung von Selectionssort ein Struktogramm. Sie können davon ausgehen, dass die Zahlenfolge in einer Reihung vorliegt.
    1. Gehe schrittweise die Zahlenfolge durch (for-Schleife)
    2. Merke dir den Index und das Element an der Position entsprechend des gegenwärtigen Schrittes als das kleinste Element (in zwei Variablen speichern)
    3. Gehe schrittweise die Zahlenfolge ab einem Element nach dem kleinsten durch (for-Schleife)
    4. Wenn das aktuelle Element kleiner ist, als das kleinste Element, merke die das aktuelle Element als neues kleinstes Element (if-Anweisung)
    5. Ist man die Zahlen durchlaufen, tausche das kleinste Element mit dem aktuell vordersten Element.
  1. In der Online IDE wurde Ihnen der Workspace Sortierverfahren mit den Dateien Sort und SortTest freigegeben.
  2. Analysieren sie zunächst, unter Zuhilfenahme der Filmdosen oder von Zahlenkarten, den Algorithmus ab Zeile 28. Dokumentieren Sie hier die Schritte, die durchgeführt werden.

    Implementieren Sie für die Zahlenfolge 34, 7, 23, 89, 12, 56, 3, 78, 45, 21 mindestens einen weiteren Sortieralgorithmus (Selectionsort oder Insertionsort), der eine Ganzzahlreihung aufsteigend sortiert und testen Sie diesen mit der oben genannten Zahlenfolge.

    Wenn Zeichenketten sortiert werden sollen, muss geklärt werden, was es genau bedeutet, dass eine Zeichenkette "größer" als eine andere ist. Dazu gibt es die folgenden Regeln:

    Definition lexikografisch

    Ein Wort ist lexikografisch größer als ein anderes Wort, wenn es in der alphabetischen Reihenfolge nach dem anderen Wort kommt.
    Die lexikografische Ordnung basiert auf dem Vergleich der Zeichen in den Wörtern von links nach rechts, ähnlich wie in einem Wörterbuch.
    Hier sind die Regeln für den lexikografischen Vergleich:

    1. Zeichenvergleich: Vergleiche die Zeichen der beiden Wörter nacheinander. Das Wort, das das erste Zeichen hat, das im Alphabet weiter hinten steht, ist lexikografisch größer.
    2. Länge der Wörter: Wenn alle Zeichen bis zur Länge des kürzeren Wortes gleich sind und das längere Wort zusätzliche Zeichen hat, ist das längere Wort lexikografisch größer.

    Beispiele:
    "Apfel" ist lexikografisch kleiner als "Banane", weil 'A' vor 'B' im Alphabet kommt.
    "Apfel" ist lexikografisch kleiner als "Apfelsine", weil das erste Wort kürzer ist und alle Zeichen übereinstimmen.
    In Java vergleicht man zwei Zeichenketten mit der Zeichenkettenoperation compareTo(String s):

    String wort1 = "Hawaii";
    String wort2 = "Tokyo";
    String wort3 = "tokyo";
    println(wort1.compareTo(wort2)); // Ausgabe: -12
    println(wort2.compareTo(wort2)); // Ausgabe: 0
    println(wort2.compareTo(wort1)); // Ausgabe: 12
    println(wort2.compareTo(wort3)); // Ausgabe: -32
    
    

    Fazit: Ist das erste Wort lexikografisch größer als das zweite, ist das Ergebnis von compareTo positiv.
    Ist das erste Wort lexikografisch kleiner als das zweite, ist das Ergebnis von compareTo negativ.
    Sind beide Worte identisch, ist das Ergebnis von compareTo 0.
    Zusatz: Der zurückgegebene Wert entspricht dem Unterschied in den ASCII-Werten der ersten Zeichen in beiden Wörtern, in denen diese sich unterscheiden. Im Beispiel "Hawaii" und "Tokyo" hat das 'H' den ASCII-Wert 72 und das 'T' den Wert 84. Deshalb ist bei compareTo zwischen "Hawaii" und "Tokyo" das Ergebnis -12, sowie bei compareTo zwischen "Tokyo" und "Hawaii" das Ergebnis 12.
    Die Kleinbuchstaben beginnen bei 97 und die Großbuchstaben bei 65, weshalb Kleinbuchstaben lexikografisch größer, als Großbuchstaben sind.
    Gegeben ist die Folge der Wörter Apfel, Banane, Kiwi, Orange, Traube, Erdbeere, Himbeere, Pfirsich, Mango, Zitrone.
    Implementieren Sie mindestens einen Sortieralgorithmus, um eine Zeichenkettenreihung zu sortieren und testen Sie Ihre Lösung mit der oben genannten Folge von Zeichenketten.

    Quelle: In Anlehnung an Vorlesung Algorithmen und Datenstrukturen Universität Oldenburg 2011

    Entwickeln Sie zum Bubblesortverfahren einen zweiten effizienteren Algorithmus, der abbricht, sobald keine Vertauschungen in einem Schleifendurchlauf gemacht wurden. Testen Sie Ihre Lösung.

  1. Stellen Sie sich vor, Sie spielen ein Computerspiel, in dem Sie mit Ihrer Spielfigur zeichnen können. In Ihrem Inventar befinden sich acht Buntstifte (siehe Abbildung rechts).
    Die Stifte sind unterschiedlich stark abgenutzt. Da wir Ordnung lieben, möchten wir sie nach ihrer verbleibenden Länge sortieren: Der kürzeste Stift soll ganz links liegen, die folgenden Stifte werden dann immer länger. Um es einfach zu halten, nehmen wir an, dass keine zwei Stifte exakt gleich lang sind.
    Technisch betrachtet können wir uns die Buntstifte als eine Reihung von Objekten vorstellen, die wir nach ihrer Größe ordnen möchten.
    Buntstifte
  2. Sortieren Sie einen Satz Buntstifte, die sich sich vorne abholen können, mit allen Ihnen bekannten Sortierverfahren so wie oben beschrieben.

    Die Buntstifte sollen nun mit einer Klasse Buntstift modelliert werden.Unten finden Sie zwei verschiedene Szenarien.
    Entscheiden Sie sich für eine Variante.

    Klassenkarte der Klasse Buntstift

    Implementieren Sie die Klasse Buntstift und erstellen Sie in einer weiteren Klasse StifteTest die oben abgebildete Buntstiftreihung stifte.

    Implementieren Sie in der Klasse StifteTest je einen Algorithmus (keine Operation), der die Buntstiftreihung stifte wie folgt sortiert:

    1. Aufsteigend, nach der Stiftlänge.
    2. Absteigend, nach der lexikografischen Ordnung der Farben.

    Klassenkarte der Klassen Buntstifte und Color

    Erstellen Sie eine Klasse StifteTestSchwierig, in der Sie eine Zeichenketten-Reihung farbenNamen mit den oben abgebildeten Stftfarben erstellen.
    Erstellen Sie weiterhin eine Color-Reihung farben, welche an den entsprechenden Indizes passende Color-Objekte enthält. Zur Farbe "Rot" wäre z. B. ein Color-Objekt mit den Attributen red: 250, green: 0 und blue: 0 geeignet.
    Implementieren Sie außerdem die Klasse Buntstift und erstellen Sie in der Klasse StifteTestSchwierig die oben abgebildete Buntstiftereihung stifte.

    Implementieren Sie in der Klasse StifteTestSchwierig je einen Algorithmus (keine Operation), der die Buntstiftreihung stifte wie folgt sortiert:

    1. Aufsteigend, nach der Stiftlänge.
    2. Aufsteigend, nach der lexikografischen Ordnung der Farben.
    3. Absteigend, nach dem Mittelwert der drei Farbkomponenten.

  1. (eA, optional) Auf einer Autobahn wird über Kameras die Verkehrslage überwacht. Eine Software registriert und speichert die vorhanden Autos und deren Position zueinander in Reihung belegung.
  2. Schreiben Sie ein Programm, welches eine Reihung (Array) der Größe 50 mit integer-Zufallszahlen ohne Beschränkung füllt. Verschiedene Autotypen werden durch die abgespeicherten Zahlenwerte unterschieden. Dieses Array soll zunächst einfach ausgegeben werden.
    (Schwieriger: In der Reihung darf keine Zahl mehr als zweimal vorkommen!)

    Jetzt soll eine Operation reindraengeln implementiert werden, der eine Ganzzahlreihung, eine Zahl (neues Auto) und eine Position übergeben werden. Das neue Auto soll dann an der richtigen Position der Reihung eingefügt werden, wobei ab der eingegebenen Position alle bisherigen Elemente um eine Position nach hinten verschoben werden. Die neue Reihung wird zurückgegeben.

  1. (eA, optional) Im Folgenden werden einigen Sortierungen für das Adventureszenarioe implementiert. Dazu bietet es sich an für die Klassen, im Rahmen dieser Aufgabe, Minimal-Lösungen zu implementieren.
  2. Die Helden müssen eine Reihung von Quests nach ihrer Dringlichkeit und Belohnung sortieren. Die Quests sollen zuerst nach Dringlichkeit und dann nach Belohnung sortiert werden.
    Erstellen Sie die Klasse Quest entsprechend.
    Verwendet den Insertion-Sort-Algorithmus aus der Klasse Sort und die Quests mit den Attributen: [(Dringlichkeit: 3, Belohnung: 100), (Dringlichkeit: 1, Belohnung: 200), (Dringlichkeit: 2, Belohnung: 150), (Dringlichkeit: 1, Belohnung: 100)]
    Testet in einem weiteren Programm denn Insertion-Sort-Algorithmus. Achten Sie darauf, zuerst nach Dringlichkeit und dann nach Belohnung zu sortieren.
    Dokumentieren Sie die Einfügeposition in jedem Schritt durch eine entsprechende Konsolenausgabe.

    Verändert die Lösung aus a) so, dass du eine Operation sortQuest implementierst, welche eine Zeichenkette übergeben bekommt und diese passend einsortiert.
    Je nach Übergabe, wird zuerst nach Dringlichkeit oder zuerst nach Belohnung sortiert.
    Das Ergebnis wird ausgegeben.

    Sortiert eine Reihung von Waffen nach ihrem Gewicht, um die leichtesten Waffen für schnelle Angriffe zu finden, mit Hilfe des Cocktail-Shaker- Sort-Algorithmus, der unten in Form eines Struktogramms gegeben ist. Nutzt die folgende Reihung der Waffen: [15, 5, 25, 10, 20].
    Implementiert Cocktail-Shaker-Sort-Algorithmus in der Klasse Sort und nutzt ihn in einem Testprogramm.

    Struktogramm des Cocktail-Shaker-Sort-Algorithmus

    Sortiert eine Reihung von Rüstungen nach ihrem Schutzwert, um die beste Verteidigung zu gewährleisten, mit Hilfe des Comb-Sort-Algorithmus, der unten in Wortform beschrieben ist.
    Nutzt die folgende Reihung der Rüstungen: [30, 10, 40, 20, 50]
    Implementiert den Comb-Sort-Algorithmus in der Klasse Sort und testet ihn in einem weiteren Programm.
    Der Comb-Sort-Algorithmus ist eine Verbesserung des Bubble-Sort-Algorithmus, die darauf abzielt, die Effizienz zu erhöhen, indem größere Lücken zwischen den zu vergleichenden Elementen verwendet werden. Der Algorithmus beginnt mit einer großen Lücke und reduziert diese schrittweise, bis sie eins erreicht. In jedem Durchlauf werden Elemente, die durch die aktuelle Lücke getrennt sind, verglichen und gegebenenfalls vertauscht. Dies hilft, größere Elemente schneller nach oben zu bewegen und kleinere nach unten, wodurch die Gesamtzahl der Durchläufe reduziert wird.

    Comb-Sort-Algorithmus, entnommen von Wikipedia

    Sortiert eine Reihung von Schätzen nach ihrem Wert, um die wertvollsten Schätze zuerst zu sichern, mit Hilfe des Gnome-Sort-Algorithmus, der unten in Form eines Struktogramms gegeben ist.
    Nutzt die folgende Reihung der Schätze: [100, 50, 150, 200, 75]
    Implementieren Sie den Gnome-Sort-Algorithmus in der Klasse Sort und testen Sie ihn in einem weiteren Programm.

    Comb-Sort-Algorithmus, entnommen von Wikipedia

    Sortiert eine Reihung von Dörfern nach ihrer Entfernung vom Hauptquartier, um die schnellsten Routen zu planen, mit Hilfe des Shell-Sort- Algorithmus, der unten in Wortform gegeben ist.
    Nutzt die folgende Reihung der Dörfer [12, 5, 8, 3, 10]
    Implementiert den Shell-Sort-Algorithmus in der Klasse Sort und testet ihn in einem weiteren Programm.
    Der Shell-Sort-Algorithmus ist eine Erweiterung des Insertion-Sort-Algorithmus, die größere Lücken zwischen den zu vergleichenden Elementen verwendet. Der Algorithmus beginnt mit einer großen Lücke und reduziert diese schrittweise, bis sie eins erreicht. In jedem Durchlauf werden Elemente, die durch die aktuelle Lücke getrennt sind, verglichen und gegebenenfalls vertauscht. Dies hilft, die Reihung schneller zu sortieren, indem größere Elemente schneller nach oben und kleinere nach unten bewegt werden. Der Shell-Sort-Algorithmus ist effizienter als der einfache Insertion-Sort, insbesondere bei größeren Reihungen.

Für Rettungseinsätze nach Katastrophen, wie z. B. Erdbeben, soll eine Suchdrohne programmiert werden. Die Drohne hat verschiedene Sensoren und soll nach Überlebenden in Trümmern suchen. Das Katastrophengebiet wird durch ein Gitter modelliert und die Drohne kann einzelne Sektoren mit ihren Sensoren scannen.
Der Scan eines Sektors liefert ein Objekt der Klasse Scan, die mit den Namen sxy, wobei x die entsprechende Zeile und y die Spalte des Gitters ist.

x↓ / y→ 0 1 2 3 ...
0 s00 s01 s02 s03 ...
1 s10 s11 s12 s13 ...
2 s20 s21 s22 s23 ...
3 s30 s31 s32 s33 ...
... ... ... ... ... ...
Einteilung des Katastrophengebiets in einzelne Sektoren

Ein Objekt der Klasse Scan enthält eine einzelne Reihung mit verschiedenen Sensorwerten vom Inhaltstyp Ganzzahlen. Um eine Entscheidung treffen zu können, ob sich in dem Sektor Überlebende befinden, muss die Drohne nach bestimmten Werten suchen und auch deren relative Häufigkeit berechnen können.
Objekte der Klasse Scan haben die Attribute bezeichnung, in Form einer Zeichenkette und daten in Form einer Ganzzahl-Reihung.
Der Konstruktor setzt bezeichnung auf den übergebenen Wert und initialisiert daten als Ganzzahl-Reihung der Länge 500.
Die Operationen getBezeichnung und getDaten geben die Attribute zurück.
Die Operation setDaten(index : Ganzzahl, wert : Ganzzahl) ersetzt den aktuellen Wert am Index index von daten durch wert.
Die Operation setDaten(neu : Ganzzahl[]) überschreibt daten mit neu.
Eine Klassenkarte der Klasse Scan ist weiter unten abgebildet.
Das Gitter des Katastrophengebiets liegt global als zweidimensionale Reihung gitter vom Inhhaltstyp Scan vor.

Klassenkarte der Klasse Scan
  1. Angenommen, die Werte der Sensoren liegen in einer der beiden Varianten als Ganzzahl-Reihung daten vor, von denen jeweils ein Ausschnitt abgebildet ist:
    3
    8
    5
    0
    2
    7
    10
    1
    4
    6
    unsortierte Reihung
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    sortierte Reihung
    Verschiedene Reihungen

    Wenn es Überlebende im gescannten Sektor geben sollte, muss die Drohne nach bestimmten Werten in der Reihung suchen.
    Je schneller das geht, desto höher sind die Chancen, Überlebende rechtzeitig zu entdecken und eventuell retten zu können.
    Im Folgenden sind zwei Operationen zur Suche gegeben:

    public int suche1(int[] reihung, int gesucht) {
        for (int i = 0; i < reihung.length; i++) {
            if (reihung[i] == gesucht) {
                return i;
            }
        }
        return -1;
    }
    
    Operation zum suchen: Variante 1
    
    public int suche2(int[] reihung, int gesucht) {
        int links = 0;
        int rechts = reihung.length - 1;
        while (links <= rechts) {
            int mitte = links + (rechts - links) / 2;
            if (reihung[mitte] == gesucht) {
                return mitte;
            }
            else {
                if (reihung[mitte] < gesucht) {
                    links = mitte + 1;
                }
                else {
                    rechts = mitte - 1;
                }
            }
        }
        return -1;
    }
    
    
    Operation zum suchen: Variante 2
  2. Untersuchen Sie, ob man beide Algorithmen auf beide Reihungen anwenden kann.

    Die beiden Algorithmen finden in verschiedenen Situationen einen bestimmten Wert in einer Reihung.
    Nun untersuchen Sie, wie aufwendig die beiden Verfahren im Vergleich zueinander sind. Wenn man die Laufzeit eines Algorithmus untersuchen möchte, kann man die Dauer, wie lange dieser Algorithmus läuft, zählen. Die Ausführungszeit wird in der Online-IDE immer ausgegeben, wenn ein Programm vollständig abgelaufen ist:

    Zeitmessung der Laufzeit eines Algorithmus in der Online-IDE

    Implementieren Sie ein Programm Suchalgorithmen, in dem es beide Suchoperationen als Operationen gibt.
    Testet die Operationen mit einer Ganzzahl-Reihung von Zufallszahlen der Größe 20 und einer sortierten Ganzzahl-Reihung der Größe 20.

    Jetzt soll untersucht werden, wie sich die Laufzeit in Abhängigkeit der Anzahl der Elemente in den Reihungen verändert.
    Die folgenden Operationen helfen bei der Erzeugung von Reihung mit Zufallszahlen und geben diese zurück.

    public int lineareSuche(int[] reihung, int gesucht) {
        for (int i = 0; i < reihung.length; i++) {
            if (reihung[i] == gesucht) {
                return i;
            }
        }
        return -1;
    }
    
    public int binaereSuche(int[] reihung, int gesucht) {
        int links = 0;
        int rechts = reihung.length - 1;
        while (links <= rechts) {
            int mitte = links + (rechts - links) / 2;
            if (reihung[mitte] == gesucht) {
                return mitte;
            } else {
                if (reihung[mitte] < gesucht) {
                    links = mitte + 1;
                } else {
                    rechts = mitte - 1;
                }
            }
        }
        return -1;
    }
    
    public int[] sortierteReihung(int anzahl) {
        int[] reihe = new int[anzahl];
        int count = 0;
        while (count < anzahl) {
            int zufallszahl = (int) (Math.random() * reihe.length);
            if (!istInReihe(reihe, zufallszahl, count)) {
                reihe[count] = zufallszahl;
                count++;
            }
        }
        // Sortiere die Reihe
        bubbleSort(reihe);
        return reihe;
    }
    
    public int[] zufallsReihung(int anzahl) {
        int[] reihe = new int[anzahl];
        int count = 0;
        while (count < anzahl) {
            int zufallszahl = (int) (Math.random() * reihe.length);
            if (!istInReihe(reihe, zufallszahl, count)) {
                reihe[count] = zufallszahl;
                count++;
            }
        }
        return reihe;
    }
    
    // Prueft, ob eine Zahl bereits in der Reihe vorhanden ist
    private boolean istInReihe(int[] reihe, int zahl, int count) {
        for (int i = 0; i < count; i++) {
            if (reihe[i] == zahl) {
                return true; // Zahl ist bereits vorhanden
            }
        }
        return false; // Zahl ist nicht vorhanden
    }
    
    // Bubble Sort Methode
    public void bubbleSort(int[] array) {
        int n = array.length;
        boolean swapped;
        for (int i = 0; i < n - 1; i++) {
            swapped = false;
            for (int j = 0; j < n - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    // Vertausche die Elemente
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    swapped = true;
                }
            }
            if (!swapped) {
                break;
            }
        }
    }
    
    Operationen zur Erstellung von Ganzzahl-Reihung
    

    Erweitern Sie Ihr Programm so, dass Sie zunächst

    • für eine nicht sortierte Reihung die Laufzeit mit der linearen Suche und für eine sortierte Reihung die Laufzeit mit der binären Suche messen und aufschreiben.
    • Anschließend sollen 10.000 Durchgänge gemessen, dann die durchschnittliche Laufzeit berechnet und aufgeschrieben werden.

    Laufzeit in Nanosekunden
    Sortierverfahren Größe des Arrays
    100 200 400 800 1600 3200 6400 12800 25600
    Lineare Suche
    Binäre Suche
    Messung der Algorithmen-Laufzeit (Nanosekunden)

    In dieser Teilaufgabe untersuchen Sie theoretisch die Veränderung der Laufzeit, in Abhängigkeit der Größe der Reihung.
    Untersuchung der linearen Suche:
    Angenommen, eine unsortierte Reihhung hat 5 Elemente:

    5
    3
    1
    -2
    4

    Überlegen Sie, wie häufig die lineare Suche eine Zahl in den folgenden Fällen vergleichen muss:

    • Die Zahl 5 wird gesucht.
    • Die Zahl 4 wird gesucht.
    • Die Zahl 1 wird gesucht.

    Nun verdoppelt sich die Anzahl der Elemente in der Reihung:

    5
    3
    1
    -2
    4
    3
    9
    8
    7
    6

    Geben Sie an, wie sich der Aufwand verändert, wenn 5, 4 oder 6 gesucht werden.

    Untersuchung der binären Suche:
    Angenommen, eine sortierte Reihhung hat 5 Elemente:

    2
    4
    5
    7
    9

    Überlegen Sie, wie häufig die binäre Suche eine Zahl in den folgenden Fällen vergleichen muss:

    • Die Zahl 5 wird gesucht.
    • Die Zahl 2 wird gesucht.
    • Die Zahl 9 wird gesucht.

    Nun verdoppelt sich die Anzahl der Elemente in der Reihung:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

    Geben Sie an, wie sich der Aufwand verändert, wenn 4, 7 oder 0 gesucht werden.

    Ordnen Sie die folgenden Aussagen einem der beiden Suchverfahhren zu:

    1. Im schlimmsten Fall müssen alle n Elemente durchsucht werden, was zu einer maximalen Anzahl von Vergleichen führt. Dies bedeutet, dass die Anzahl der Vergleiche linear zur Größe der Reihung wächst.
    2. Bei jedem Vergleich wird die Größe des Suchraums halbiert.
    3. Es wird ein Teile-und-Herrsche-Ansatz verwendet, bei dem das Suchproblem in kleinere Teilprobleme zerlegt wird.
    4. Diese Suche funktioniert bei alle, auch unsortierten, Reihungen.

    Effizienz der Binären Suche

    1. Teile-und-Herrsche-Ansatz: Die binäre Suche verwendet einen Teile-und-Herrsche-Ansatz, bei dem das Suchproblem in kleinere Teilprobleme zerlegt wird. Sie vergleicht das gesuchte Element mit dem mittleren Element der Reihung und entscheidet basierend auf diesem Vergleich, ob das gesuchte Element in der linken oder rechten HälOe der Reihung liegt.
    2. Reduzierung des Suchraums: Bei jedem Vergleich wird die Größe des Suchraums halbiert.
    3. Effzienz bei großen Datenmengen: Aufgrund der Wachstumsrate ist die binäre Suche besonders effizient bei großen Datenmengen. Selbst bei sehr großen Reihungen bleibt die Anzahl der nötigen Vergleiche relativ gering.

    Ineffizienz der Linearen Suche

    1. Einfachheit: Die lineare Suche ist ein einfacher Algorithmus, der jedes Element der Reihung der Reihe nach durchläuft, bis das gesuchte Element gefunden wird oder das Ende der Reihung erreicht ist.
    2. Vollständige Durchsuchung: Im schlimmsten Fall muss die lineare Suche alle ( n ) Elemente durchsuchen, was zu einer maximalen Anzahl von Vergleichen führt. Dies bedeutet, dass die Anzahl der Vergleiche linear zur Größe der Reihung wächst.
    3. Unsortierte Daten: Da die lineare Suche keine Annahmen über die Anordnung der Elemente trifft, ist sie bei unsortierten Daten die einzige Möglichkeit, um sicherzustellen, dass das gesuchte Element gefunden wird. Dies führt dazu, dass sie ineffizienter ist, insbesondere wenn die Reihung groß ist.

    Fazit
    Die binäre Suche ist effizienter als die lineare Suche, da sie die Anzahl der Vergleiche reduziert, indem sie den Suchraum bei jedem Schritt halbiert. Dies macht sie besonders gut geeignet für große, sortierte Datenmengen. Die lineare Suche hingegen ist ineffizient für große oder unsortierte Datenmengen, da sie im schlimmsten Fall alle Elemente durchsuchen muss.

  1. In dieser Aufgabe untersucht ihr auf theoretischer Ebene, wie verschiedene Kontrollstrukturen die Laufzeit von Algorithmen beeinflussen.
    Ordnet die folgenden Aussagen den acht Beispielalgorithmen zu.
    Laufzeitkomplexität, Grafik erstellt mit Gemini
  2. Scan s00 = gitter[0][0];
    int[] sensorData = s00.getDaten();
    int threshold = 100;
    boolean foundSurvivor = (sensorData[0] > threshold);
    
    

    Scan s00 = gitter[0][0];
    int[] sensorData = s00.getDaten();
    for (int i = 0; i < sensorData.length; i++) {
        if (sensorData[i] > 100) {
            println("Möglicher Überlebender gefunden bei Index: " + i);
        }
    }
    
    

    int n = gitter.length;
    for (int x = 0; x < n; x++) {
        for (int y = 0; y < n; y++) {
            Scan sector = gitter[x][y];
            int[] sensorData = sector.getDaten();
        }
    }
    for (int i = 0; i < sensorData.length; i++) {
        if (sensorData[i] > 100) {
            println("Überlebender in Sektor " + sector.getBezeichnung() + " gefunden.");
            break;
        }
    }
    
    

    int n = gitter.length;
    int m = gitter[0].length;
    for (int x = 0; x < n; x++) {
        for (int y = 0; y < m; y++) {
            Scan sector = gitter[x][y];
            int[] sensorData = sector.getDaten();
        }
    }
    for (int i = 0; i < sensorData.length; i++) {
        if (sensorData[i] > 100) {
            println("Überlebender in Sektor " + sector.
            getBezeichnung() + " gefunden.");
            break;
        }
    }
    
    

    Scan s00 = gitter[0][0];
    int[] sensorData = s00.getDaten();
    sensorData[0] = 150;
    sensorData[1] = 200;
    println("Sensorwerte aktualisiert.");
    
    

    Scan s00 = gitter[0][0];
    int[] sensorData = s00.getDaten();
    if (sensorData[0] > 100) {
        println("Möglicher Überlebender im Sektor " + s00.getBezeichnung());
    }
    
    

    Scan s00 = gitter[0][0];
    int[] sensorData = s00.getDaten();
    if (sensorData[0] > 100) {
        println("Möglicher Überlebender im Sektor " + s00.getBezeichnung());
    } else {
        println("Keine Überlebenden im Sektor " + s00.getBezeichnung());
    }
    
    

    Scan s00 = gitter[0][0];
    String sectorType = s00.getBezeichnung();
    switch (sectorType) {
        case "Wohngebiet":
            println("Scanne Wohngebiet.");
            break;
        case "Industriegebiet":
            println("Scanne Industriegebiet.");
            break;
        default:
            println("Unbekannter Sektor.");
            break;
    }
    
    

    In der Informatik ist die Analyse der Laufzeit von Algorithmen entscheidend, um deren Effzienz zu bewerten. Verschiedene Algorithmen haben unterschiedliche Laufzeitklassen, die beschreiben, wie sich die Ausführungszeit in Abhängigkeit von der Größe der Eingabedaten verändert. Hier sind einige der häufigsten Laufzeitklassen:
    1. Konstante Laufzeit
    Ein Algorithmus hat eine konstante Laufzeit, wenn die Ausführungszeit unabhängig von der Größe der Eingabedaten ist. Dies bedeutet, dass der Algorithmus immer die gleiche Zeit benötigt, um ausgeführt zu werden, egal wie groß die Datenmenge ist. Ein typisches Beispiel ist der direkte Zugriff auf ein Element in einer Reihung, da dieser Zugriff immer in der gleichen Zeit erfolgt.
    2. Lineare Laufzeit
    Ein Algorithmus hat eine lineare Laufzeit, wenn die Ausführungszeit direkt proportional zur Anzahl der Eingabedaten ist. Das bedeutet, dass sich die Laufzeit verdoppelt, wenn sich die Anzahl der Daten verdoppelt. Ein Beispiel hierfür ist die lineare Suche in einer Reihung, bei der jedes Element einmal überprüft werden muss.
    3. Quadratische Laufzeit
    Ein Algorithmus hat eine quadratische Laufzeit, wenn die Ausführungszeit proportional zum Quadrat der Anzahl der Eingabedaten ist. Dies tritt häufig bei Algorithmen auf, die geschachtelte Schleifen verwenden, bei denen jede Schleife über die gesamte Datenmenge iteriert. Ein Beispiel ist der Bubble Sort, bei dem jedes Element mit jedem anderen verglichen wird.
    4. Logarithmische Laufzeit
    Ein Algorithmus hat eine logarithmische Laufzeit, wenn die Ausführungszeit proportional zur Anzahl der Schritte ist, die erforderlich sind, um die Datenmenge durch wiederholtes Halbieren zu reduzieren. Dies bedeutet, dass die Laufzeit nur langsam ansteigt, selbst wenn die Datenmenge stark zunimmt. Ein Beispiel ist die binäre Suche, bei der die Datenmenge in jedem Schritt halbiert wird, um ein Element zu finden.Ein Algorithmus hat eine logarithmische Laufzeit, wenn die Ausführungszeit proportional zur Anzahl der Schritte ist, die erforderlich sind, um die Datenmenge durch wiederholtes Halbieren zu reduzieren. Dies bedeutet, dass die Laufzeit nur langsam ansteigt, selbst wenn die Datenmenge stark zunimmt. Ein Beispiel ist die binäre Suche, bei der die Datenmenge in jedem Schritt halbiert wird, um ein Element zu finden.
    Zusammengesetzte Algorithmen
    Bei zusammengesetzten Algorithmen, die aus mehreren Kontrollstrukturen bestehen, wie Schleifen in Verzweigungen, wird die Laufzeit durch die Kombination der einzelnen Komponenten bestimmt. Die Analyse erfolgt in der Regel durch:

    • Aufwand für den Test: Die Zeit, die benötigt wird, um die Bedingung der Verzweigung zu überprüfen.
    • Maximum vom Aufwand für Alternative 1 und Alternative 2: Die Laufzeit wird durch den maximalen Aufwand der beiden Alternativen bestimmt, da im schlimmsten Fall die aufwendigere Alternative ausgeführt wird.

    Diese Analyse hilft, die Effizienz von Algorithmen zu verstehen und zu optimieren, indem sie die Auswirkungen der Struktur und der Kontrollflussentscheidungen auf die Gesamtlaufzeit berücksichtigt.

    Laufzeitkomplexität nachh KOntrollstrukturen, Grafik erstellt mit Gemini