java\02_Algorithmen\.
In jeder der 9 ausgewerteten Prüfungen steht in Teil 2 (Aufgabe 1 oder 2) eine große Algorithmusaufgabe mit 25–30 Punkten. Fast immer bekommst du ein Klassendiagramm mit Attributen („Getter sind vorhanden“), eine Beispieltabelle mit Daten und eine Methodensignatur. Gesucht ist eine Methode, die eine Liste oder ein Array von Objekten durchläuft und dabei zählt, summiert, filtert, ein Ergebnis-Array füllt oder ein Minimum sucht. Ob du das in Pseudocode, als Struktogramm oder in einer Programmiersprache schreibst, ist meist freigestellt. Der Belegsatz enthält nur die Pseudocode-Konventionen, nicht die Muster. Die musst du können.
| Unterthema | Kategorie | Typische Punkte | Wichtigste Fähigkeit |
|---|---|---|---|
| ALG-01 Algorithmus in Pseudocode | Immer | 25–30 (T2), 4–15 (T1) | Grundmuster sicher anwenden, sauber initialisieren |
| ALG-02 Arrays und Listen | Immer | in ALG-01 enthalten | Indexgrenzen, 2D-Arrays, List<T>-Methoden |
| ALG-03 Minimum/Maximum, Suche | Oft | 10–25 | richtig initialisieren, Objekt statt Wert merken |
| ALG-04 Speicherbedarf, Zahlensysteme | Wahrscheinlich | 3–7 | 1024er-Präfixe, Bit/Byte, Rechenweg mit Einheiten |
| ALG-05 Sortieren | Wahrscheinlich | 10–25 | Bubblesort, Vergleichsfunktion |
| ALG-06 Zeichenketten | Wahrscheinlich | 5–8 | Zeichen prüfen, Text zerlegen |
| ALG-07 Exceptions | Wahrscheinlich | 2–6 | Fehlerfälle erkennen und behandeln |
| ALG-08 Rekursion | Sonstige | bis 12 | Basisfall, Schreibtischtest |
| ALG-09 Nebenläufigkeit | Sonstige | 4 | Race Condition, Sperren |
Vorkommen: 9/9 · Prüfungen: alle (W21–S26), Teil 2 Aufgabe 1 oder 2 mit 25–30 Punkten · zusätzlich kleinere Methoden in Teil 1 (S22, S23, S24, W24)
k.getPreis(), nicht k.preis.... return ergebnis). Halte dich an die vorgegebenen Namen und Typen.date.between(start, ende) (S26) oder DateTime.compare() (W24). Steht eine solche Methode in der Aufgabe, wird ihre Verwendung erwartet.List<T> mit add, get, size, contains (eventuell remove). Andere Listenmethoden solltest du nicht voraussetzen.Der Belegsatz (S24) gibt nur die Grundformen vor. Für alles andere gilt: lesbar, eindeutig und konsequent. Diese Schreibweise wird in diesem Block durchgehend verwendet:
| Baustein | Pseudocode | Java |
|---|---|---|
| Variable anlegen | anzahl : integer := 0 | int anzahl = 0; |
| Zuweisung | summe := summe + x | summe = summe + x; |
| Vergleich | = <> < > <= >= | == != < > <= >= |
| Logik | und oder nicht | && || ! |
| Verzweigung | wenn … dann … sonst … Ende wenn | if (…) { … } else { … } |
| Zählschleife | zähle i von 0 bis n - 1 … Ende zähle | for (int i = 0; i <= n - 1; i++) |
| Kopfgesteuert | solange … Ende solange | while (…) { … } |
| Fußgesteuert | wiederhole … solange … | do { … } while (…); |
| Ganzzahldivision / Rest | a div b, a mod b | a / b (bei int), a % b |
| Objekt erzeugen | new Artikel(…), new List<Artikel>() | new Artikel(…), new ArrayList<>() |
| Array anlegen / Länge | new integer[5], feld.length | new int[5], feld.length |
| Methode / Rückgabe | funktion name(p : typ) : typ … rückgabe x … Ende funktion | typ name(typ p) { … return x; } |
| „kein Objekt“ | null | null |
Ende zähle statt Ende-zähle kostet praktisch nie Punkte. Ein Zähler, der in der falschen Schleife auf 0 gesetzt wird, kostet dagegen viele.Fast jede Prüfungsaufgabe ist eine Kombination dieser Muster. Beispiel-Szenario hier: ein Lager mit Objekten der Klasse Artikel (Attribute artikelNr, bezeichnung, kategorie (1–4), bestand, mindestbestand, preis; Getter vorhanden).
Muster 1 – Zählen mit Bedingung (Rückgabe integer): Zähler vor der Schleife auf 0, in der Schleife bei erfüllter Bedingung um 1 erhöhen.
funktion zaehleNachbestellung(artikel : List<Artikel>) : integer
anzahl : integer := 0
zähle i von 0 bis artikel.size() - 1
a : Artikel := artikel.get(i)
wenn a.getBestand() < a.getMindestbestand() dann
anzahl := anzahl + 1
Ende wenn
Ende zähle
rückgabe anzahl
Ende funktion
Muster 2 – Summe und Durchschnitt (Rückgabe double): Summe und Anzahl mitführen, erst nach der Schleife teilen und den Fall „keine passenden Elemente“ abfangen.
funktion durchschnittspreis(artikel : List<Artikel>, kategorie : integer) : double
summe : double := 0.0
anzahl : integer := 0
zähle i von 0 bis artikel.size() - 1
wenn artikel.get(i).getKategorie() = kategorie dann
summe := summe + artikel.get(i).getPreis()
anzahl := anzahl + 1
Ende wenn
Ende zähle
wenn anzahl = 0 dann
rückgabe 0.0 // keine Division durch 0
Ende wenn
rückgabe summe / anzahl
Ende funktion
Muster 3 – Filtern in eine neue Liste (Rückgabe List<T>): leere Ergebnisliste anlegen, passende Objekte mit add anhängen. Die Originalliste bleibt unverändert.
funktion nachbestellListe(artikel : List<Artikel>) : List<Artikel>
ergebnis : List<Artikel> := new List<Artikel>()
zähle i von 0 bis artikel.size() - 1
a : Artikel := artikel.get(i)
wenn a.getBestand() < a.getMindestbestand() dann
ergebnis.add(a)
Ende wenn
Ende zähle
rückgabe ergebnis
Ende funktion
Muster 4 – Ergebnis-Array „je …“ (Rückgabe integer[]): Ein Array, in dem jeder Index für eine Gruppe steht (Kategorie, Stunde, Strecke …). Der Wert des Datensatzes bestimmt den Index. Achte auf die Verschiebung, wenn die Gruppen bei 1 beginnen, der Index aber bei 0.
funktion artikelJeKategorie(artikel : Artikel[]) : integer[]
ergebnis : integer[] := new integer[4] // Index 0..3 = Kategorie 1..4, alle 0
zähle i von 0 bis artikel.length - 1
k : integer := artikel[i].getKategorie()
ergebnis[k - 1] := ergebnis[k - 1] + 1
Ende zähle
rückgabe ergebnis
Ende funktion
Muster 5 – Eindeutige Liste und verschachtelte Schleifen. Aus Rohdaten mit Wiederholungen (viele Bestellungen je Lieferant) soll eine Liste mit genau einem Objekt je Schlüssel entstehen, danach wird für jedes dieser Objekte ein Wert aus allen Rohdaten berechnet. Beispiel: List<Bestellung> (Attribute lieferant, betrag) wird zu List<LieferantSumme> (Attribute lieferant, summe; Konstruktor LieferantSumme(lieferant) setzt summe auf 0).
// Schritt 1: jeden Lieferanten genau einmal aufnehmen
funktion erzeugeUebersicht(bestellungen : List<Bestellung>) : List<LieferantSumme>
uebersicht : List<LieferantSumme> := new List<LieferantSumme>()
zähle i von 0 bis bestellungen.size() - 1
name : string := bestellungen.get(i).getLieferant()
gefunden : boolean := false
zähle j von 0 bis uebersicht.size() - 1
wenn uebersicht.get(j).getLieferant() = name dann
gefunden := true
Ende wenn
Ende zähle
wenn nicht gefunden dann
uebersicht.add(new LieferantSumme(name))
Ende wenn
Ende zähle
rückgabe uebersicht
Ende funktion
// Schritt 2: für jedes Übersichtsobjekt (außen) alle Bestellungen (innen) durchlaufen
funktion berechneSummen(bestellungen : List<Bestellung>,
uebersicht : List<LieferantSumme>)
zähle i von 0 bis uebersicht.size() - 1
ls : LieferantSumme := uebersicht.get(i)
summe : double := 0.0 // für JEDES Objekt neu auf 0!
zähle j von 0 bis bestellungen.size() - 1
wenn bestellungen.get(j).getLieferant() = ls.getLieferant() dann
summe := summe + bestellungen.get(j).getBetrag()
Ende wenn
Ende zähle
ls.setSumme(summe)
Ende zähle
Ende funktion
integer → Zähler oder Summe, double → Durchschnitt, List → Filter, Array → „je Index“, Objekt → Suche/Minimum, void → vorhandene Objekte per Setter ändern.zähle i von 0 bis liste.size() - 1 bzw. bis feld.length - 1.> oder >=?).null), dann rückgabe.summe / anzahl mit zwei integer liefert in Java 7 / 2 = 3. Summe als double führen oder umwandeln ((double) summe / anzahl).zähle i von 0 bis liste.size() greift auf ein Element zu, das es nicht gibt. Richtig: bis liste.size() - 1.rückgabe in der Schleife beendet die Methode beim ersten Treffer. Das ist nur bei einer Suche gewollt.= (Vergleich) und := (Zuweisung) verwechselt; Attribut direkt statt über den Getter gelesen.add in die Liste gelegt, oder Liste/Array am Ende nicht zurückgegeben.contains), dann je Objekt den Durchschnitt in einem Zeitraum berechnen (Muster 5 + 2, verschachtelte Schleifen, Hilfsmethode between).equals kümmern.Vorkommen: 9/9 · Arrays in 8/9 (2D-Arrays: W21, S22, S23), Listen List<T> in 5/9 mit steigender Tendenz (S22, W22, S24, W25, S26)
new integer[24]. Die Größe kann sich danach nicht mehr ändern.null). In Pseudocode schreibst du die Vorbelegung zur Sicherheit dazu, wenn du dich darauf verlässt.ArrayIndexOutOfBoundsException, das Programm bricht ab (siehe ALG-07).length - 2 laufen, sonst greift feld[i + 1] im letzten Durchlauf ins Leere.Ein 2D-Array ist eine Tabelle: daten[zeile][spalte]. daten.length ist die Anzahl der Zeilen, daten[0].length die Anzahl der Spalten. Typische Prüfungsbeispiele: Tag × Stunde, Monat × Verbraucher. Beispiel hier: Kino mit 3 Sälen (Zeilen) und 4 Vorstellungen am Tag (Spalten), gespeichert wird die Besucherzahl.
besucher[saal][vorstellung] mit Zeilen- und SpaltensummenZeilensummen (außen Zeilen)
zeilen : integer[]
zeilen := new integer[besucher.length]
zähle z von 0 bis besucher.length - 1
summe : integer := 0
zähle s von 0 bis besucher[z].length - 1
summe := summe + besucher[z][s]
Ende zähle
zeilen[z] := summe
Ende zähle
Spaltensummen (außen Spalten)
spalten : integer[]
spalten := new integer[besucher[0].length]
zähle s von 0 bis besucher[0].length - 1
summe : integer := 0
zähle z von 0 bis besucher.length - 1
summe := summe + besucher[z][s]
Ende zähle
spalten[s] := summe
Ende zähle
Merkhilfe: Die äußere Schleife läuft über das, wofür du je ein Ergebnis willst. Willst du je Saal eine Summe, läuft außen der Saal. Für eine Gesamtsumme oder ein Maximum über alle Zellen ist die Reihenfolge egal, dann reicht eine Variable außerhalb beider Schleifen.
List<T>Eine Liste wächst und schrumpft dynamisch. In der Prüfung ist sie meist als Klasse List<T> mit wenigen Methoden vorgegeben (T steht für den Elementtyp, z. B. List<Artikel>). In Java entspricht das List<T> bzw. ArrayList<T>.
| Methode | Wirkung | Beispiel |
|---|---|---|
add(t : T) : void | hängt t hinten an | ergebnis.add(a) |
get(i : integer) : T | liefert das Element an Position i (ab 0) | artikel.get(0) |
size() : integer | Anzahl der Elemente | zähle i von 0 bis artikel.size() - 1 |
contains(t : T) : boolean | true, wenn ein gleiches Element enthalten ist | wenn nicht namen.contains(n) dann … |
remove(i : integer) : void | entfernt Element i, alle folgenden rücken eine Position nach vorn | liste.remove(3) |
Duplikate vermeiden: Vor dem add prüfen, ob das Element schon da ist. Bei einfachen Werten (Zahlen, Strings) geht das direkt mit contains. Bei Objekten vergleicht contains in Java mit equals, und das prüft ohne Überschreiben nur, ob es dasselbe Objekt ist. Wenn die Aufgabe nicht ausdrücklich sagt, dass contains z. B. „namensgleiche“ Objekte erkennt (so in S26), suchst du mit einer eigenen inneren Schleife nach dem Schlüssel (Muster 5 in ALG-01).
namen : List<string> := new List<string>()
zähle i von 0 bis bestellungen.size() - 1
n : string := bestellungen.get(i).getLieferant()
wenn nicht namen.contains(n) dann
namen.add(n)
Ende wenn
Ende zähle
Elemente beim Durchlaufen entfernen: Nach remove(i) rutscht das nächste Element auf Position i. Zählt die Schleife danach normal weiter (i + 1), wird dieses Element nie geprüft.
remove (✗ = soll entfernt werden)Drei saubere Lösungen: (1) rückwärts laufen (von size() - 1 bis 0), dann betrifft die Verschiebung nur schon geprüfte Elemente; (2) mit solange laufen und i nur erhöhen, wenn nichts entfernt wurde; (3) gar nicht entfernen, sondern die gewünschten Elemente in eine neue Liste filtern (Muster 3).
i : integer := liste.size() - 1
solange i >= 0
wenn liste.get(i).istAbgelaufen() dann
liste.remove(i)
Ende wenn
i := i - 1
Ende solange
| Kriterium | Array | Liste (List<T>) |
|---|---|---|
| Größe | fest beim Anlegen | dynamisch, wächst mit add |
| Anzahl Elemente | feld.length (Attribut) | liste.size() (Methode) |
| Zugriff | feld[i] | liste.get(i) |
| Einfügen/Löschen | nur durch Umkopieren | add, remove eingebaut |
| Elementtypen (Java) | auch primitive Typen (int[]) | nur Objekte (List<Integer>) |
| Geeignet für | feste Struktur: 24 Stunden, 12 Monate, 7 Tage | unbekannte Anzahl: Ergebnislisten, Filter |
length (Array, Attribut), length() (String, Methode) und size() (Liste, Methode) verwechselt. In Pseudocode wird das toleriert, in Java nicht.daten[stunde][tag] statt daten[tag][stunde]. Immer an der Deklaration der Aufgabe orientieren.daten.length begrenzt (das ist die Zeilenzahl!).get(i) auf eine leere Liste.contains).Vorkommen: 4/9 · Prüfungen: W21, W23, S24, W25 (dazu Min/Max in S22 innerhalb der 2D-Aufgabe) · Ø 21 Punkte
Prinzip: Man merkt sich den „bisher besten“ Wert und vergleicht jedes weitere Element damit. Entscheidend ist der Startwert.
| Startwert | So geht's | Geeignet, wenn … | Risiko |
|---|---|---|---|
| erstes Element | min := feld[0]…, Schleife ab Index 1 | alle Elemente zählen und mindestens eins da ist | leere Liste/Array → Laufzeitfehler |
null bzw. Merker | bestes := null, Vergleich wenn bestes = null oder … | nur Elemente mit Bedingung zählen (z. B. nur freie) | Aufrufer muss null behandeln |
| Extremwert | min := 999999 bzw. Integer.MAX_VALUE | Obergrenze der Werte ist sicher bekannt | wirkt geraten; falscher Wert, wenn nichts passt |
| 0 (!) | min := 0 | nie für ein Minimum positiver Werte | Ergebnis ist immer 0 |
funktion kleinsterBestand(artikel : Artikel[]) : integer
min : integer := artikel[0].getBestand() // Startwert: erstes Element
zähle i von 1 bis artikel.length - 1
wenn artikel[i].getBestand() < min dann
min := artikel[i].getBestand()
Ende wenn
Ende zähle
rückgabe min
Ende funktion
Wird nicht der Wert, sondern das Objekt oder seine Position gesucht („welcher Artikel hat den kleinsten Bestand?“), merkst du dir den Index (minIndex := i) oder das Objekt selbst und vergleichst mit dessen Getter. Bei Gleichstand entscheidet < (erstes gefundenes bleibt) oder <= (letztes gewinnt).
min := 0 und dann wenn wert < min: Bei positiven Werten ist die Bedingung nie erfüllt, das Ergebnis ist immer 0. Genau diesen Fehler sollte man in W25 T2 A3 in einer gegebenen Minimum-Funktion finden. Beim Maximum geht 0 als Startwert nur gut, wenn alle Werte sicher ≥ 0 sind; bei Temperaturen im Winter z. B. nicht.
Die Elemente werden der Reihe nach geprüft, bis das gesuchte gefunden ist. Aufwand im schlechtesten Fall: alle n Elemente (O(n)). Wird nichts gefunden, gibt man ein vereinbartes „nicht gefunden“ zurück: null bei Objekten, -1 bei Indizes, false bei Ja/Nein-Fragen.
funktion sucheArtikel(artikel : List<Artikel>, nr : integer) : Artikel
i : integer := 0
solange i < artikel.size() und artikel.get(i).getArtikelNr() <> nr
i := i + 1
Ende solange
wenn i < artikel.size() dann
rückgabe artikel.get(i)
Ende wenn
rückgabe null // nicht gefunden
Ende funktion
Wichtig ist die Reihenfolge der Bedingungen: erst den Index prüfen, dann zugreifen. Bei und wird der zweite Teil nicht mehr ausgewertet, wenn der erste falsch ist (Kurzschlussauswertung). Alternativen mit gleicher Wirkung: eine Zählschleife mit rückgabe direkt beim Treffer, oder eine boolesche Variable gefunden als Abbruchbedingung. Eine Zählschleife ohne Abbruch funktioniert auch, prüft aber unnötig alle weiteren Elemente.
Gesucht ist das Objekt mit der kleinsten Entfernung zu einem Punkt. Die Entfernung wird für jedes Element erst berechnet (Satz des Pythagoras), dann wie ein normales Minimum behandelt. Kommt eine Bedingung dazu („nur freie Taxis“), brauchst du den Startwert null, weil das erste Element vielleicht gar nicht in Frage kommt.
d = √((x₂ − x₁)² + (y₂ − y₁)²)
funktion naechstesFreiesTaxi(taxis : List<Taxi>, fx : double, fy : double) : Taxi
bestes : Taxi := null
minAbstand : double := 0.0 // erst gültig, wenn bestes <> null
zähle i von 0 bis taxis.size() - 1
t : Taxi := taxis.get(i)
wenn t.istFrei() dann
dx : double := t.getX() - fx
dy : double := t.getY() - fy
d : double := wurzel(dx * dx + dy * dy)
wenn bestes = null oder d < minAbstand dann
bestes := t
minAbstand := d
Ende wenn
Ende wenn
Ende zähle
rückgabe bestes // null, wenn kein Taxi frei ist
Ende funktion
Variante der Minimumsuche, z. B. in W25 („Tisch, dessen Platzzahl am wenigsten über der Personenzahl liegt“). Eigenes Beispiel: Für einen Umzug mit 14,5 m³ Ladung wird der kleinste Transporter gesucht, der noch reicht. Zuerst filtert eine Bedingung die ungeeigneten aus (volumen >= bedarf), dann wird unter den übrigen die kleinste Differenz gesucht.
funktion passendesFahrzeug(fahrzeuge : List<Fahrzeug>, bedarf : double) : Fahrzeug
bestes : Fahrzeug := null
zähle i von 0 bis fahrzeuge.size() - 1
f : Fahrzeug := fahrzeuge.get(i)
wenn f.getVolumen() >= bedarf dann
wenn bestes = null oder f.getVolumen() < bestes.getVolumen() dann
bestes := f
Ende wenn
Ende wenn
Ende zähle
rückgabe bestes
Ende funktion
Ist die Abweichung in beide Richtungen erlaubt (z. B. „Wert, der am nächsten an 20 liegt“), vergleichst du den Betrag der Differenz: abs(wert - 20).
null nicht dokumentiert.liste.get(i)… vor i < liste.size()).Vorkommen: 3/9 · Prüfungen: W22 (Dezimal → Binär, Datentypen), W23 (Speicherbedarf in GiB, 7 P), S26 (Speicherbedarf in TiB, 5 P) · W23 und S26 waren fast identisch
| SI (dezimal) | Byte | IEC (binär) | Byte | Unterschied |
|---|---|---|---|---|
| 1 kB (Kilobyte) | 10³ = 1 000 | 1 KiB (Kibibyte) | 2¹⁰ = 1 024 | 2,4 % |
| 1 MB (Megabyte) | 10⁶ = 1 000 000 | 1 MiB (Mebibyte) | 2²⁰ = 1 048 576 | 4,9 % |
| 1 GB (Gigabyte) | 10⁹ | 1 GiB (Gibibyte) | 2³⁰ = 1 073 741 824 | 7,4 % |
| 1 TB (Terabyte) | 10¹² | 1 TiB (Tebibyte) | 2⁴⁰ = 1 099 511 627 776 | 10,0 % |
| Dezimal | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Binär | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
| Hex | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
Dezimal → Binär (Divisionsrestverfahren): fortlaufend durch 2 teilen, Reste notieren, von unten nach oben lesen. Beispiel 156:
156 : 2 = 78 Rest 0 78 : 2 = 39 Rest 0 39 : 2 = 19 Rest 1 19 : 2 = 9 Rest 1 9 : 2 = 4 Rest 1 4 : 2 = 2 Rest 0 2 : 2 = 1 Rest 0 1 : 2 = 0 Rest 1 ↑ lesen 156 = 1001 1100 (binär)
Binär → Dezimal (Stellenwerte): jede 1 mit ihrem Stellenwert 2ᵏ multiplizieren und addieren:
Stelle: 128 64 32 16 8 4 2 1 Bit: 1 0 1 1 0 0 1 1 = 128 + 32 + 16 + 2 + 1 = 179
Binär ↔ Hex: von rechts in 4er-Gruppen teilen, jede Gruppe ist eine Hex-Ziffer: 1011 0011 = B3; 1001 1100 = 9C.
Hex → Dezimal: B3 = 11 · 16 + 3 = 179. Dezimal → Hex: durch 16 teilen: 179 : 16 = 11 Rest 3 → B3.
| Java-Typ | Größe | Wertebereich / Genauigkeit | typischer Einsatz | SQL-Gegenstück |
|---|---|---|---|---|
boolean | logisch 1 Bit (JVM meist 1 Byte) | true / false | Merker, Status | BOOLEAN / TINYINT(1) |
byte | 1 Byte | −128 … 127 | kleine Zähler, Prozent | TINYINT |
short | 2 Byte | −32 768 … 32 767 | Jahreszahl, Minuten eines Tages | SMALLINT |
int | 4 Byte | ca. ±2,1 Milliarden | IDs, Mengen, Cent-Beträge | INT |
long | 8 Byte | ca. ±9,2 · 10¹⁸ | Zeitstempel in ms, große Summen | BIGINT |
float | 4 Byte | ca. 7 signifikante Stellen | Messwerte, wenn Genauigkeit reicht | FLOAT |
double | 8 Byte | ca. 15–16 signifikante Stellen | Berechnungen, Durchschnitte | DOUBLE |
char | 2 Byte | ein UTF-16-Zeichen | einzelnes Zeichen | CHAR(1) |
String | variabel | Java intern ca. 1–2 Byte je Zeichen | Texte | VARCHAR(n), CHAR(n) |
Auswahlregel: den kleinsten Typ, dessen Wertebereich sicher reicht (mit Reserve für die Zukunft). Geldbeträge nie als float/double (Rundungsfehler bei 0,1), sondern als DECIMAL(10,2) in der Datenbank bzw. in Cent als ganze Zahl. Nummern mit führenden Nullen oder Sonderzeichen (PLZ, Telefonnummer) sind Text.
| Medium | Formel (unkomprimiert) |
|---|---|
| Bild | Breite × Höhe (Pixel) × Farbtiefe (Bit/Pixel) ÷ 8 = Byte. Farbtiefe = Kanäle × Bit je Kanal (RGB mit 8 Bit je Kanal = 24 Bit) |
| Video | Bildgröße (Byte) × Bilder pro Sekunde (fps) × Sekunden |
| Audio | Abtastrate (Hz) × Bittiefe (Bit) × Kanäle × Sekunden ÷ 8 |
| Komprimiert | unkomprimierte Größe ÷ Kompressionsfaktor (z. B. „1 : 10“ → ÷ 10) |
Beispiel Dokumentenscanner: A4-Seite mit 300 dpi = 2 480 × 3 508 Pixel, 24 Bit Farbtiefe, 400 Seiten pro Tag, 250 Arbeitstage.
Pixel je Seite = 2 480 · 3 508 = 8 699 840 Pixel Bit je Seite = 8 699 840 Pixel · 24 Bit/Pixel = 208 796 160 Bit Byte je Seite = 208 796 160 Bit : 8 Bit/Byte = 26 099 520 Byte MiB je Seite = 26 099 520 : 1 024 : 1 024 ≈ 24,89 MiB Byte pro Jahr = 26 099 520 · 400 · 250 = 2 609 952 000 000 Byte GiB pro Jahr = 2 609 952 000 000 : 1 024³ ≈ 2 430,71 GiB TiB pro Jahr = 2 430,71 : 1 024 ≈ 2,37 TiB
Video: Full HD 1 920 × 1 080, 24 Bit, 25 fps, 1 Minute: 1 920 · 1 080 · 3 Byte = 6 220 800 Byte je Bild; · 25 · 60 = 9 331 200 000 Byte ≈ 8,69 GiB pro Minute. Als Datenrate: 155 520 000 Byte/s · 8 = 1 244,16 Mbit/s (Datenraten immer mit SI-Präfix). Audio: CD-Qualität 44 100 Hz · 16 Bit · 2 Kanäle = 1 411 200 Bit/s = 176 400 Byte/s; 3 Minuten = 31 752 000 Byte ≈ 30,28 MiB.
Vorkommen: 2/9 · Prüfungen: W22 (T2 A1, 25 P: Bubblesort → Vergleichsfunktion → generisch), S24 (T2 A2a, 12 P: Sortieren mit übergebener Vergleichsfunktion) · Tendenz eher fallend
Benachbarte Elemente werden verglichen und vertauscht, wenn sie in der falschen Reihenfolge stehen. Nach jedem Durchlauf ist das größte noch unsortierte Element ganz hinten angekommen („aufgestiegen wie eine Blase“). Deshalb kann die innere Schleife mit jedem Durchlauf ein Element früher aufhören.
funktion bubbleSort(zahlen : integer[])
zähle i von 0 bis zahlen.length - 2
zähle j von 0 bis zahlen.length - 2 - i
wenn zahlen[j] > zahlen[j + 1] dann
hilf : integer := zahlen[j] // Dreieckstausch
zahlen[j] := zahlen[j + 1]
zahlen[j + 1] := hilf
Ende wenn
Ende zähle
Ende zähle
Ende funktion
Optimierung mit Abbruch: Eine boolesche Variable getauscht wird zu Beginn jedes Durchlaufs auf false gesetzt und beim Tauschen auf true. Wurde in einem Durchlauf nichts getauscht, ist das Feld sortiert und man kann aufhören (äußere Schleife dann als solange getauscht bzw. wiederhole … solange getauscht). Bei bereits sortierten Daten braucht Bubblesort so nur einen Durchlauf.
Selectionsort: Im unsortierten Rest das Minimum suchen und mit dem ersten Element des Rests tauschen.
zähle i von 0 bis n - 2
minPos : integer := i
zähle j von i + 1 bis n - 1
wenn feld[j] < feld[minPos] dann
minPos := j
Ende wenn
Ende zähle
tausche feld[i] und feld[minPos]
Ende zähle
Insertionsort: wie beim Kartenspielen – das nächste Element nehmen, größere nach rechts schieben, in die Lücke einfügen.
zähle i von 1 bis n - 1
wert : integer := feld[i]
j : integer := i - 1
solange j >= 0 und feld[j] > wert
feld[j + 1] := feld[j]
j := j - 1
Ende solange
feld[j + 1] := wert
Ende zähle
| Verfahren | Idee | bester Fall | schlechtester Fall | stabil? |
|---|---|---|---|---|
| Bubblesort | Nachbarn tauschen | O(n) mit Abbruch | O(n²) | ja |
| Selectionsort | Minimum nach vorne | O(n²) | O(n²) | nein (Tausch über Distanz) |
| Insertionsort | einsortieren | O(n) | O(n²) | ja |
| Mergesort | halbieren, sortieren, zusammenmischen | O(n log n) | O(n log n) | ja (braucht Zusatzspeicher) |
| Quicksort | Pivot wählen, aufteilen | O(n log n) | O(n²) (im Mittel n log n) | nein |
Größenordnung: Bei 1 000 Elementen sind n² = 1 000 000 Vergleiche, n · log₂ n ≈ 10 000. Für große Datenmengen nimmt man deshalb die Sortierfunktion der Bibliothek (Java: List.sort, Arrays.sort; für Objekte stabil).
Stabilität: Ein Verfahren ist stabil, wenn Elemente mit gleichem Sortierschlüssel ihre bisherige Reihenfolge behalten. Beispiel: Eine Bücherliste ist alphabetisch nach Titel sortiert und wird danach stabil nach Preis sortiert → Bücher mit gleichem Preis stehen weiterhin alphabetisch. Bubblesort ist stabil, solange nur bei > (nicht bei >=) getauscht wird.
Bei Objekten vergleichst du ein Attribut über den Getter, tauschst aber das ganze Objekt: wenn buecher[j].getPreis() > buecher[j + 1].getPreis() dann … Hilfsvariable vom Typ Buch.
Soll derselbe Algorithmus nach verschiedenen Kriterien sortieren können, übergibt man ihm eine Vergleichsfunktion (Java: Comparator). Der Algorithmus fragt nur noch „steht a vor b?“, das Kriterium steckt in der Funktion. Vertrag der Vergleichsfunktion vergleiche(a, b) : integer:
| Rückgabe | Bedeutung | Sortieralgorithmus |
|---|---|---|
| negativ (z. B. −1) | a gehört vor b | nicht tauschen |
| 0 | gleichwertig | nicht tauschen (Stabilität) |
| positiv (z. B. 1) | a gehört hinter b | tauschen: wenn vergleiche(feld[j], feld[j + 1]) > 0 dann … |
funktion nachPreisAufsteigend(a : Buch, b : Buch) : integer
wenn a.getPreis() < b.getPreis() dann
rückgabe -1
Ende wenn
wenn a.getPreis() > b.getPreis() dann
rückgabe 1
Ende wenn
rückgabe 0
Ende funktion
// mehrstufig: erst Autor, bei gleichem Autor nach Titel
funktion nachAutorDannTitel(a : Buch, b : Buch) : integer
wenn a.getAutor() <> b.getAutor() dann
rückgabe a.getAutor().compareTo(b.getAutor())
Ende wenn
rückgabe a.getTitel().compareTo(b.getTitel())
Ende funktion
Absteigend sortieren: die Rückgabewerte vertauschen (1 statt −1) oder die Funktion mit (b, a) statt (a, b) rechnen. In Java:
Comparator<Buch> nachPreis = (a, b) -> Double.compare(a.getPreis(), b.getPreis());
Comparator<Buch> nachAutorDannTitel =
Comparator.comparing(Buch::getAutor).thenComparing(Buch::getTitel);
buecher.sort(nachPreis.reversed()); // teuerstes zuerst
// generische Sortiermethode (W22): für jede Liste mit passendem Comparator
static <T> void sortiere(List<T> liste, Comparator<T> vergleiche) {
... wenn vergleiche.compare(a, b) > 0 ist, a und b tauschen ...
}
Vorteile der übergebenen Vergleichsfunktion: ein getesteter Sortieralgorithmus für beliebig viele Sortierkriterien (keine Code-Duplikate), neue Kriterien ohne Änderung am Algorithmus (Open-Closed-Prinzip, Strategie-Muster), mit Generics für beliebige Klassen nutzbar.
length - 1 → feld[j + 1] greift über das Ende hinaus.a := b; b := a überschreibt a).< 0 statt > 0) → absteigend statt aufsteigend.boolean zurück statt einer Zahl; bei double-Werten (int)(a - b) rechnen (0,4 wird zu 0 → „gleich“).List<T> (7 P).wenn vergleiche(…) > 0).Vorkommen: 2/9 · Prüfungen: W21 (Passwortprüfung im Struktogramm, Fehler finden), W25 (Namensspalte per SUBSTRING aufteilen)
Ein String ist eine Folge von Zeichen (char). Wie beim Array beginnt der Index bei 0, das letzte Zeichen hat den Index length() - 1. Strings sind in Java unveränderlich: Methoden wie trim() oder substring() liefern einen neuen String.
d = "14.09.2026" mit Indizes| Methode (Java, im Pseudocode genauso) | Wirkung | Beispiel mit d = "14.09.2026" |
|---|---|---|
length() | Anzahl Zeichen | 10 |
charAt(i) | Zeichen an Position i | charAt(0) = '1' |
substring(von, bis) | Teilstring von von bis bis − 1 | substring(6, 10) = "2026" |
indexOf(x) | erste Position von x, sonst −1 | indexOf('-') = −1 |
split(trenner) | zerlegt in ein String-Array | 3 Teile "14", "09", "2026" (Java: split("\\."), weil der Punkt ein Regex-Zeichen ist) |
trim() | Leerzeichen am Anfang/Ende entfernen | " Bonn ".trim() = "Bonn" |
equals(t), equalsIgnoreCase(t) | Inhaltsvergleich | d.equals("14.09.2026") = true |
compareTo(t) | alphabetischer Vergleich (<0, 0, >0) | "Abel".compareTo("Berg") < 0 |
startsWith, endsWith, contains | Teiltext prüfen | d.endsWith("2026") = true |
toUpperCase(), replace(a, b) | umwandeln, ersetzen | d.replace('.', '/') = "14/09/2026" |
Character.isDigit(c), isLetter, isUpperCase, isLowerCase | Zeichenklasse prüfen | isDigit('4') = true |
c - '0', Integer.parseInt(s) | Ziffer bzw. Text in Zahl | '7' - '0' = 7, parseInt("2026") = 2026 |
Zwei Varianten, die man auseinanderhalten muss:
false zurückgeben, nach der Schleife true.boolean) vor der Schleife auf false, bei Treffer auf true, erst nach der Schleife auswerten.Beispiel Benutzername: 5 bis 20 Zeichen, beginnt mit einem Buchstaben, danach nur Buchstaben, Ziffern oder Unterstrich, mindestens eine Ziffer.
funktion pruefeBenutzername(name : string) : boolean
wenn name.length() < 5 oder name.length() > 20 dann
rückgabe false
Ende wenn
wenn nicht istBuchstabe(name.charAt(0)) dann
rückgabe false
Ende wenn
hatZiffer : boolean := false
zähle i von 1 bis name.length() - 1
c : char := name.charAt(i)
wenn istZiffer(c) dann
hatZiffer := true
sonst
wenn nicht istBuchstabe(c) und c <> '_' dann
rückgabe false // unerlaubtes Zeichen: sofort Schluss
Ende wenn
Ende wenn
Ende zähle
rückgabe hatZiffer // "mindestens eine": erst nach der Schleife
Ende funktion
Trennzeichen suchen (indexOf), links und rechts davon mit substring herausschneiden, Leerzeichen mit trim entfernen. Immer überlegen, was passiert, wenn das Trennzeichen fehlt (indexOf liefert −1).
// "53111 Bonn" -> PLZ und Ort
pos : integer := adresse.indexOf(' ')
wenn pos = -1 dann
// Fehlerfall: kein Leerzeichen -> z. B. Ausnahme oder Rückgabe null
sonst
plz : string := adresse.substring(0, pos)
ort : string := adresse.substring(pos + 1, adresse.length()).trim()
Ende wenn
Mit split geht es kürzer, wenn das Trennzeichen eindeutig ist: teile := "14.09.2026".split(".") → teile[0] = "14", teile.length = 3.
In Java vergleicht == bei Objekten die Referenzen (dasselbe Objekt im Speicher?), equals den Inhalt. Zwei inhaltsgleiche Strings, die z. B. aus einer Eingabe oder Datei stammen, sind verschiedene Objekte → == liefert false. Deshalb Strings in Java immer mit equals vergleichen. Im Pseudocode der Prüfung wird = meist akzeptiert (S26 erlaubt ausdrücklich ==). Alphabetische Reihenfolge: compareTo. Zahlen, die als Text vorliegen, werden alphabetisch verglichen: "10" < "9"! Vorher umwandeln.
Zusammenhang mit SQL (W25 T2 A4d): Dort zerlegt man Spalten mit SUBSTRING(spalte, start, länge) – Achtung, in SQL beginnt die Zählung bei 1 und der dritte Parameter ist die Länge, nicht das Ende. Position eines Zeichens: LOCATE(' ', name) bzw. INSTR.
substring(von, bis): bis ist ausgeschlossen. substring(0, 3) liefert 3 Zeichen.charAt(length()) → Indexfehler; letzter Index ist length() - 1.'A' (ein Zeichen, char) und "A" (String) verwechselt; in Java ist c == "A" ein Compilerfehler.sonst rückgabe false beantwortet → schon das erste Nicht-Ziffer-Zeichen führt zu false.ALTER TABLE um Vor-/Nachname ergänzen und per UPDATE … SUBSTRING befüllen.Vorkommen: 2/9 · Prüfungen: W21, W25 (Fehlerfall bzw. null behandeln) · verwandt: W24 T2 A2a (Fehlerantwort mit HTTP-Statuscode erzeugen)
Eine Exception (Ausnahme) ist ein Objekt, das einen Fehlerzustand beschreibt. Sie wird an der Fehlerstelle geworfen (throw) und wandert die Aufrufkette hinauf, bis ein passender catch-Block sie fängt. Fängt sie niemand, bricht das Programm mit einer Fehlermeldung (Stacktrace) ab. Vorteil gegenüber Fehlercodes: Normaler Ablauf und Fehlerbehandlung sind getrennt, und ein Fehler kann nicht „aus Versehen“ ignoriert werden.
Java
try {
int menge = Integer.parseInt(eingabe);
lager.entnehmen(artikelNr, menge);
System.out.println("Gebucht.");
} catch (NumberFormatException e) {
System.out.println("Bitte eine ganze Zahl eingeben.");
} catch (BestandZuNiedrigException e) {
System.out.println(e.getMessage());
} finally {
protokoll.schliessen(); // läuft IMMER
}
Pseudocode (eine mögliche Schreibweise)
try
menge : integer := zuZahl(eingabe)
lager.entnehmen(artikelNr, menge)
ausgabe("Gebucht.")
catch (NumberFormatException e)
ausgabe("Bitte eine ganze Zahl eingeben.")
catch (BestandZuNiedrigException e)
ausgabe(e.getMessage())
finally
protokoll.schliessen()
Ende try
return im try-Block)| checked (geprüft) | unchecked (ungeprüft) | |
|---|---|---|
| Oberklasse | Exception (aber nicht RuntimeException) | RuntimeException (und Error) |
| Compiler verlangt Behandlung? | ja: fangen (try/catch) oder mit throws in der Methodensignatur weiterreichen | nein |
| Ursache typischerweise | äußere Umstände, die auch korrekter Code nicht verhindern kann | Programmierfehler, die man durch Prüfungen vermeiden kann |
| Beispiele | IOException, FileNotFoundException, SQLException, eigene fachliche Ausnahmen | NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException, IllegalArgumentException, NumberFormatException |
throw wirft eine Ausnahme (throw new IllegalArgumentException("Menge muss positiv sein")), throws kündigt in der Signatur an, dass eine Methode eine checked Exception weiterreichen kann. Eigene Ausnahmen erben von Exception (checked) oder RuntimeException (unchecked):
public class BestandZuNiedrigException extends Exception {
public BestandZuNiedrigException(String meldung) { super(meldung); }
}
public void entnehmen(int menge) throws BestandZuNiedrigException {
if (menge <= 0) {
throw new IllegalArgumentException("Menge muss positiv sein"); // unchecked
}
if (menge > bestand) {
throw new BestandZuNiedrigException("Nur " + bestand + " Stück"); // checked
}
bestand = bestand - menge;
}
| Fehlerfall | erkennen | sinnvolle Reaktion |
|---|---|---|
Parameter ist null | wenn liste = null dann | IllegalArgumentException werfen (sonst später NullPointerException) |
| leere Liste / nichts passt | size() = 0, Zähler = 0 | dokumentierten Sonderwert zurückgeben (null, 0.0, leere Liste) oder eigene Exception |
| Division durch 0 | Nenner vorher prüfen | bei int wirft Java ArithmeticException; bei double entsteht ohne Fehler Infinity bzw. NaN – also selbst prüfen! |
| ungültiger Wert (negativ, außerhalb des Bereichs) | Bereichsprüfung am Methodenanfang | IllegalArgumentException |
| ungültiger Index | i < 0 oder i >= size() | vorher prüfen; sonst IndexOutOfBoundsException |
| Text ist keine Zahl | Integer.parseInt | NumberFormatException fangen, Benutzer informieren |
| Datei, Datenbank, Netzwerk nicht erreichbar | checked Exceptions | fangen: Meldung, Protokoll, ggf. erneuter Versuch; Ressourcen im finally schließen |
Exception oder Sonderwert? Ein Sonderwert (null, −1) ist einfach und in der Prüfung oft verlangt („liefert null, wenn kein Tisch frei ist“), muss aber vom Aufrufer geprüft werden – vergisst er es, knallt es später an anderer Stelle. Eine Exception erzwingt die Behandlung (checked) und trägt eine Fehlermeldung, ist aber für „normale“ Ergebnisse wie „nichts gefunden“ oft zu schwer. Faustregel: erwartbares Ergebnis → Sonderwert, echter Fehler/Vertragsbruch → Exception. Prüfungen am Methodenanfang („Wächter“, fail fast) halten den eigentlichen Algorithmus übersichtlich.
catch (Exception e) { }): Der Fehler wird verschluckt, das Programm rechnet mit falschen Daten weiter, die Ursache ist später kaum zu finden.catch (Exception e): fängt auch Programmierfehler, die man eigentlich beheben müsste; keine gezielte Reaktion möglich.try/catch laufe immer: Wird die Ausnahme nicht gefangen, läuft nur noch der finally-Block, danach geht es beim Aufrufer weiter.null, keine Division durch 0). Beispiele: W25 T2 A1 (bester Tisch, wenn keiner passt), W21 T2 A2.finally, warum leere catch-Blöcke schlecht sind.Vorkommen: 1/9 · Prüfung: W22 T2 A2 (25 P, davon 12 P Rekursion: Schreibtischtest einer rekursiven Umwandlung Dezimal → Binär, Fehler korrigieren, Rekursion vs. Iteration; dazu iterative Lösung)
Eine Funktion ist rekursiv, wenn sie sich selbst aufruft. Das funktioniert nur mit zwei Bestandteilen: dem Basisfall (Abbruchbedingung, liefert direkt ein Ergebnis) und dem Rekursionsfall (ruft sich mit einem kleineren Problem auf und baut aus dessen Ergebnis das eigene). Jeder Aufruf bekommt eigene Parameter und lokale Variablen auf dem Aufrufstapel (Stack).
funktion fakultaet(n : integer) : integer
wenn n <= 1 dann // Basisfall
rückgabe 1
Ende wenn
// Rekursionsfall:
rückgabe n * fakultaet(n - 1)
Ende funktion
funktion quersumme(n : integer) : integer
wenn n < 10 dann // Basisfall:
rückgabe n // nur eine Ziffer
Ende wenn
// Rekursionsfall:
rückgabe (n mod 10) + quersumme(n div 10)
Ende funktion
fakultaet(4) – erst bis zum Basisfall absteigen, dann die Ergebnisse rückwärts zusammensetzenSchreibtischtest einer Rekursion: Eine Zeile je Aufruf; erst alle Aufrufe nach unten notieren, dann die Rückgabewerte von unten nach oben eintragen. Beispiel quersumme(4721):
| Aufruf | n | n mod 10 | n div 10 | Rückgabe |
|---|---|---|---|---|
| 1 | 4721 | 1 | 472 | 1 + 13 = 14 |
| 2 | 472 | 2 | 47 | 2 + 11 = 13 |
| 3 | 47 | 7 | 4 | 7 + 4 = 11 |
| 4 | 4 | Basisfall (n < 10) | 4 | |
| Rekursion | Iteration (Schleife) | |
|---|---|---|
| Lesbarkeit | oft kürzer und nah an der mathematischen Definition; natürlich für Bäume, Verzeichnisse, „Teile und herrsche“ (Mergesort, binäre Suche) | bei einfachen Abläufen leichter nachzuvollziehen |
| Speicher | jeder Aufruf belegt Platz auf dem Stack; sehr tiefe Rekursion → StackOverflowError | konstanter Speicher |
| Laufzeit | Overhead durch Funktionsaufrufe; naive Varianten rechnen Teilprobleme mehrfach (Fibonacci) | meist schneller |
| Fehlersuche | schwerer zu debuggen | einfacher |
Jede Rekursion lässt sich iterativ umschreiben (ggf. mit eigenem Stack bzw. einer Liste). Iterative Fakultät: ergebnis := 1; zähle i von 2 bis n: ergebnis := ergebnis * i.
fakultaet(n) statt fakultaet(n - 1)) → Endlosrekursion und StackOverflowError. Basisfall liefert für einen Randwert ein falsches Ergebnis (z. B. leeren Text statt „0“). Ergebnis des rekursiven Aufrufs nicht verwendet bzw. nicht zurückgegeben.Vorkommen: 1/9 · Prüfung: W21 T2 A1b (4 P: Variante zum Blockieren von Terminen bei gleichzeitigen Buchungen wählen und begründen)
Webanwendungen bearbeiten viele Anfragen gleichzeitig (in eigenen Threads oder auf mehreren Servern), alle greifen auf dieselbe Datenbank zu. Solange nur gelesen wird, ist das unproblematisch. Kritisch wird es, wenn zwei Abläufe denselben Datensatz lesen, prüfen und dann ändern.
| Begriff | Bedeutung |
|---|---|
| gemeinsame Ressource | Daten, auf die mehrere Abläufe zugreifen (Platzstatus, Kontostand, Lagerbestand) |
| kritischer Abschnitt | Codebereich, in dem die Ressource gelesen und abhängig davon verändert wird; darf nicht von zwei Abläufen gleichzeitig durchlaufen werden |
| Race Condition | Ergebnis hängt von der zufälligen zeitlichen Reihenfolge („Wettlauf“) der Abläufe ab |
| Lost Update | eine Änderung überschreibt eine andere, die dazwischen passiert ist; Folge z. B. Doppelbuchung |
| Deadlock | zwei Abläufe warten gegenseitig auf eine Sperre des anderen – beide hängen |
| Pessimistisches Sperren | Optimistisches Sperren | |
|---|---|---|
| Annahme | Konflikte sind wahrscheinlich | Konflikte sind selten |
| Ablauf | Datensatz vor dem Lesen sperren (DB: SELECT … FOR UPDATE in einer Transaktion; Java: synchronized/Lock). Andere warten, bis die Sperre freigegeben wird. | ohne Sperre lesen und Versionsnummer merken. Beim Schreiben nur ändern, wenn die Version noch dieselbe ist: UPDATE platz SET status = 'verkauft', version = version + 1 WHERE id = 712 AND version = 5. 0 geänderte Zeilen = Konflikt. |
| Vorteil | Konflikt kann gar nicht entstehen; einfach zu verstehen | keine Wartezeiten, gute Parallelität, keine Deadlocks |
| Nachteil | Wartezeiten, schlechtere Skalierung, Deadlock-Gefahr; zu lange gehaltene Sperren blockieren andere | Konflikt fällt erst beim Speichern auf → Vorgang muss wiederholt oder abgelehnt werden |
UPDATE platz SET status = 'verkauft' WHERE id = 712 AND status = 'frei' und danach die Anzahl geänderter Zeilen auswerten. Zusätzlich kann ein UNIQUE-Constraint (z. B. auf Veranstaltung + Platz in der Buchungstabelle) Doppelbuchungen in der Datenbank selbst verhindern.Enthält nur, was dir auch die IHK beilegen würde: die Pseudocode-Konventionen (wie im Belegsatz S24) und die Anlagen zur Ausgangssituation (Klassen, Testdaten, Hilfsfunktionen). Keine Erklärungen.
| Pseudocode | Beschreibung |
|---|---|
wenn [ Bedingung ] dann [ Anweisung ] sonst [ Anweisung ] Ende wenn | Verzweigung |
zähle [ Variable ] von [ Startwert ] bis [ Endwert ] [ Schleifeninhalt ] Ende zähle | Zählschleife (Iteration) |
solange [ Bedingung ] [ Schleifeninhalt ] Ende solange | Kopfgesteuerte Schleife |
wiederhole [ Schleifeninhalt ] solange [ Bedingung ] | Fußgesteuerte Schleife |
:= | Zuweisung |
=; <; >; <=; >=; <> | Vergleiche |
integer double string boolean array | Datentypen |
Die Aufgaben dieses Blocks beziehen sich auf die folgende Ausgangssituation:
Die Auenrad GmbH betreibt in der Stadt Auenfeld ein Verleihsystem für E-Bikes und elektrische Lastenräder mit sechs Stationen. Kundinnen und Kunden leihen die Räder per App aus und geben sie an einer beliebigen Station wieder zurück. Das veraltete System soll durch die Plattform „Auenrad 2.0“ ersetzt werden (Kunden-App, Server mit Datenbank, Steuerung der Stationen).
Sie sind Auszubildende/r zum Fachinformatiker/zur Fachinformatikerin Anwendungsentwicklung bei der Kessler Softwaresysteme GmbH, die die Plattform entwickelt. Sie arbeiten in folgenden Teilprojekten mit:
Tarife: Basis: 1,00 € je Ausleihe plus 0,20 € je Minute · Student: 0,10 € je Minute · Abo: je Ausleihe 30 Freiminuten, danach 0,10 € je Minute.
Einsatzbereit ist ein Rad, wenn es nicht defekt ist und sein Akkustand mindestens 30 % beträgt.
Koordinaten: Stationen und Standorte werden in einem Stadtraster in Kilometern angegeben (x nach Osten, y nach Norden). Die Entfernung ist die Luftlinie.
getAkkuStand(), getStartStation(), getRaeder(); für boolean-Attribute heißt die Get-Methode is…(), z. B. isDefekt().KundenStatistik besitzt zusätzlich Set-Methoden für anzahl, gesamtDauer und durchschnitt. Der Konstruktor belegt diese drei Attribute mit 0.startMinute: Beginn der Ausleihe in Minuten seit Mitternacht (z. B. 462 = 07:42 Uhr). dauer in Minuten, strecke in km, preis in Euro.typ ist "E-Bike" oder "Lastenrad"; akkuStand in Prozent (0 bis 100); tarif ist "Basis", "Student" oder "Abo".d.liegtZwischen(von, bis) liefert true, wenn das Datum d im Zeitraum von von bis bis liegt (beide Tage eingeschlossen), sonst false.Methoden der Klasse List<T>:
add(t) fügt das Objekt t am Ende der Liste hinzu.get(i) liefert das Objekt an Position i; das erste Objekt hat die Position 0.size() liefert die Anzahl der Objekte in der Liste.contains(t) liefert true, wenn die Liste ein Element enthält, das gleich t ist. Zahlen und Zeichenketten gelten als gleich, wenn ihr Wert gleich ist; Objekte anderer Klassen nur, wenn es sich um dasselbe Objekt handelt.remove(i) entfernt das Objekt an Position i; alle nachfolgenden Objekte rücken um eine Position nach vorn.new List<T>() erzeugt, z. B. new List<Fahrrad>().Listen und Arrays enthalten die Objekte in der Reihenfolge der Tabellenzeilen.
| stationNr | name | x | y | kapazitaet | raeder (Radnummern) |
|---|---|---|---|---|---|
| 1 | Hauptbahnhof | 2.0 | 1.0 | 20 | 101, 102, 103 |
| 2 | Marktplatz | 3.5 | 2.5 | 12 | 104, 105 |
| 3 | Uni-Campus | 6.0 | 4.0 | 16 | 106, 107, 108 |
| 4 | Hafen | 1.0 | 5.5 | 10 | 109 |
| 5 | Klinikum | 5.0 | 7.0 | 8 | 110 |
| 6 | Seepark | 8.5 | 1.5 | 10 | 111, 112 |
| radNr | typ | akkuStand | kmStand | defekt | steht an Station |
|---|---|---|---|---|---|
| 101 | E-Bike | 85 | 1234.5 | false | 1 |
| 102 | E-Bike | 15 | 2210.0 | false | 1 |
| 103 | Lastenrad | 60 | 540.2 | true | 1 |
| 104 | E-Bike | 22 | 980.7 | false | 2 |
| 105 | E-Bike | 95 | 310.0 | false | 2 |
| 106 | Lastenrad | 40 | 1502.3 | false | 3 |
| 107 | E-Bike | 8 | 3120.9 | false | 3 |
| 108 | E-Bike | 71 | 875.4 | true | 3 |
| 109 | E-Bike | 55 | 455.0 | false | 4 |
| 110 | Lastenrad | 28 | 760.8 | false | 5 |
| 111 | E-Bike | 90 | 120.3 | false | 6 |
| 112 | E-Bike | 33 | 1999.9 | false | 6 |
| kundenNr | name (Altsystem) | tarif |
|---|---|---|
| 5001 | Yilmaz, Deniz | Abo |
| 5002 | Lorenz, Jana | Basis |
| 5003 | Nowak, Piotr | Student |
| 5004 | Schulze-Ebert, Anna Lena | Abo |
| 5005 | Haas, Tom | Basis |
| ausleihNr | kundenNr | radNr | startStation | zielStation | datum | startMinute | dauer | strecke | preis |
|---|---|---|---|---|---|---|---|---|---|
| 1 | 5001 | 101 | 1 | 3 | 2026-09-14 | 462 | 18 | 4.6 | 0.00 |
| 2 | 5002 | 105 | 2 | 1 | 2026-09-14 | 485 | 12 | 2.1 | 3.40 |
| 3 | 5003 | 106 | 3 | 5 | 2026-09-14 | 750 | 75 | 9.8 | 7.50 |
| 4 | 5001 | 104 | 3 | 1 | 2026-09-14 | 1030 | 22 | 4.9 | 0.00 |
| 5 | 5004 | 110 | 5 | 4 | 2026-09-15 | 555 | 95 | 12.4 | 6.50 |
| 6 | 5005 | 109 | 4 | 2 | 2026-09-15 | 1008 | 31 | 5.0 | 7.20 |
| 7 | 5002 | 111 | 6 | 3 | 2026-09-15 | 1055 | 64 | 8.7 | 13.80 |
| 8 | 5003 | 107 | 3 | 3 | 2026-09-16 | 785 | 40 | 6.2 | 4.00 |
| 9 | 5001 | 101 | 1 | 6 | 2026-09-16 | 475 | 26 | 7.3 | 0.00 |
| 10 | 5004 | 112 | 6 | 1 | 2026-09-16 | 1100 | 33 | 7.1 | 0.30 |
| 11 | 5005 | 102 | 1 | 2 | 2026-09-17 | 492 | 9 | 1.9 | 2.80 |
| 12 | 5003 | 106 | 5 | 3 | 2026-09-17 | 640 | 58 | 9.1 | 5.80 |
Zweidimensionales Array daten : integer[][] mit 7 Zeilen (Wochentag, 0 = Montag … 6 = Sonntag) und 6 Spalten (Zeitfenster zu je 4 Stunden). Jedes Element enthält die Anzahl der Ausleihen, die in diesem Zeitfenster an Station 1 begonnen haben.
| [0] 0–4 Uhr | [1] 4–8 Uhr | [2] 8–12 Uhr | [3] 12–16 Uhr | [4] 16–20 Uhr | [5] 20–24 Uhr | |
|---|---|---|---|---|---|---|
| [0] Mo | 1 | 14 | 9 | 11 | 18 | 4 |
| [1] Di | 0 | 16 | 8 | 10 | 21 | 3 |
| [2] Mi | 2 | 15 | 11 | 9 | 19 | 5 |
| [3] Do | 0 | 13 | 10 | 12 | 22 | 6 |
| [4] Fr | 3 | 12 | 12 | 15 | 17 | 9 |
| [5] Sa | 5 | 2 | 14 | 20 | 16 | 11 |
| [6] So | 6 | 1 | 10 | 18 | 13 | 7 |
| Ausdruck / Funktion | Ergebnis |
|---|---|
a div b | ganzzahliger Anteil der Division zweier integer-Werte (17 div 5 = 3) |
a mod b | Rest der ganzzahligen Division (17 mod 5 = 2) |
und, oder, nicht | logische Verknüpfungen |
wurzel(x : double) : double | Quadratwurzel von x |
abs(x : double) : double | Betrag von x |
entfernung(x1 : double, y1 : double, x2 : double, y2 : double) : double | Luftlinie zwischen (x1|y1) und (x2|y2) in km: √((x2 − x1)² + (y2 − y1)²) |
feld.length | Anzahl der Elemente des Arrays feld; gültige Indizes 0 bis feld.length − 1 |
new integer[n] | neues integer-Array mit n Elementen, alle mit 0 belegt |
s.length() : integer | Anzahl der Zeichen der Zeichenkette s |
s.charAt(i : integer) : char | Zeichen an Position i (erstes Zeichen: Position 0) |
s.substring(von : integer, bis : integer) : string | Teilzeichenkette von Position von bis Position bis − 1 |
s.indexOf(c : char) : integer | erste Position des Zeichens c in s; −1, wenn c nicht vorkommt |
s.trim() : string | s ohne Leerzeichen am Anfang und am Ende |
istGrossbuchstabe(c : char) : boolean | true für A–Z, Ä, Ö, Ü |
istKleinbuchstabe(c : char) : boolean | true für a–z, ä, ö, ü, ß |
istBuchstabe(c : char) : boolean | true für Groß- und Kleinbuchstaben |
istZiffer(c : char) : boolean | true für 0–9 |
zahlwert(c : char) : integer | Zahlenwert einer Ziffer, z. B. zahlwert('7') = 7 |
+ bei Zeichenketten | verkettet; Zahlen werden dabei in Text umgewandelt ("A" + 1 = "A1") |
Einzelne Zeichen (Typ char) stehen in einfachen Anführungszeichen ('A'), Zeichenketten in doppelten ("AR-"). Zeichen und Zeichenketten können im Pseudocode mit = und <> verglichen werden.
status ("frei", "reserviert" oder "ausgeliehen") und eine Versionsnummer version. Jede Änderung des Datensatzes erhöht die Versionsnummer um 1.leseRad(radNr) liest Status und Versionsnummer des Rades, ohne den Datensatz zu sperren.setzeStatusWennVersion(radNr, neuerStatus, erwarteteVersion) wird von der Datenbank als eine unteilbare Operation ausgeführt: Nur wenn die gespeicherte Versionsnummer noch erwarteteVersion ist, wird der Status geändert, die Versionsnummer erhöht und true zurückgegeben. Andernfalls bleibt der Datensatz unverändert und die Methode liefert false.legeReservierungAn(radNr, kundenNr) speichert eine neue Reservierung.Alle Aufgaben beziehen sich auf die Ausgangssituation in Anlage 1. Algorithmen sind – sofern nicht anders angegeben – in Pseudocode nach den Konventionen des Belegsatzes zu entwickeln; eine Programmiersprache (z. B. Java) ist ebenfalls zulässig. Für alle Attribute stehen Get-Methoden zur Verfügung (Anlage 2).
Die Disposition möchte wissen, wie viele Räder dringend geladen werden müssen. Gezählt werden alle Räder, die nicht defekt sind und einen Akkustand unter 25 % haben (Klasse Fahrrad, Anlage 2). Der folgende Pseudocode ist unvollständig.
funktion zaehleLadebedarf(raeder : List<Fahrrad>) : integer
anzahl : integer := ____(1)____
zähle i von 0 bis ____(2)____
r : Fahrrad := raeder.get(i)
wenn ____(3)____ dann
____(4)____
Ende wenn
Ende zähle
rückgabe ____(5)____
Ende funktion
| Lücke | Inhalt |
|---|---|
| (1) | |
| (2) | |
| (3) | |
| (4) | |
| (5) |
Angelehnt an: S24 Teil 2, Aufgabe 2b (Zählfunktion über ein Array)
Die App soll anzeigen, welche Räder eines Typs sofort genutzt werden können. Die Disposition benötigt außerdem die durchschnittliche Fahrstrecke je Station.
einsatzbereit(raeder : List<Fahrrad>, typ : string, minAkku : integer) : List<Fahrrad>. Sie gibt eine neue Liste mit allen Rädern des übergebenen Typs zurück, die nicht defekt sind und mindestens den Akkustand minAkku haben. Die übergebene Liste darf nicht verändert werden.durchschnittsStrecke(ausleihen : List<Ausleihe>, stationNr : integer) : double. Sie liefert die durchschnittliche Strecke aller Ausleihen, die an der Station stationNr begonnen haben. Gibt es keine solche Ausleihe, wird 0.0 zurückgegeben.Angelehnt an: S26 Teil 2, Aufgabe 2ba (Liste filtern) und W25 Teil 2, Aufgabe 1a
Im Kundenportal von Auenrad 2.0 soll eine Statistik zum Nutzungsverhalten angezeigt werden. Außerdem möchte die Disposition wissen, an welchen Stationen lange Fahrten beginnen. Die Ausleihen liegen als Objekte der Klasse Ausleihe in einer Liste List<Ausleihe> ausleihen vor (Anlage 2, Testdaten in Anlage 3). Verwenden Sie die Klassen KundenStatistik und Datum aus Anlage 2.
langeFahrtenJeStation(ausleihen : List<Ausleihe>, anzahlStationen : integer) : integer[]. Sie liefert ein Array mit anzahlStationen Elementen. Das Element mit dem Index i enthält die Anzahl der Ausleihen mit einer Dauer von mehr als 60 Minuten, die an der Station mit der Nummer i + 1 begonnen haben.anzahlStationen = 6 ergibt sich: [0, 0, 1, 0, 1, 1].erzeugeStatistik(ausleihen : List<Ausleihe>) : List<KundenStatistik>. Sie erzeugt für jede Kundennummer, die in den Ausleihen vorkommt, genau ein Objekt der Klasse KundenStatistik, fügt es einer neuen Liste hinzu und gibt diese Liste zurück. Die Reihenfolge entspricht dem ersten Auftreten der Kundennummer. Die Werte anzahl, gesamtDauer und durchschnitt bleiben vorerst 0.| ausleihNr | kundenNr | … |
|---|---|---|
| 1 | 5001 | … |
| 2 | 5002 | … |
| 3 | 5003 | … |
| 4 | 5001 | … |
| 5 | 5004 | … |
| … | … | … |
| kundenNr | anzahl | gesamtDauer | durchschnitt |
|---|---|---|---|
| 5001 | 0 | 0 | 0.0 |
| 5002 | 0 | 0 | 0.0 |
| 5003 | 0 | 0 | 0.0 |
| 5004 | 0 | 0 | 0.0 |
| 5005 | 0 | 0 | 0.0 |
berechneStatistik(ausleihen : List<Ausleihe>, statistik : List<KundenStatistik>, von : Datum, bis : Datum) : void. Sie setzt für jedes Objekt der Liste statistik die Werte
anzahl: Anzahl der Ausleihen dieses Kunden, deren Datum im Zeitraum von von bis bis liegt,gesamtDauer: Summe der Dauer dieser Ausleihen in Minuten,durchschnitt: durchschnittliche Dauer dieser Ausleihen als Kommazahl; hat der Kunde im Zeitraum nichts ausgeliehen, ist der Wert 0.0.von = 16.09.2026 und bis = 17.09.2026:| kundenNr | anzahl | gesamtDauer | durchschnitt |
|---|---|---|---|
| 5001 | 1 | 26 | 26.0 |
| 5002 | 0 | 0 | 0.0 |
| 5003 | 2 | 98 | 49.0 |
| 5004 | 1 | 33 | 33.0 |
| 5005 | 1 | 9 | 9.0 |
Angelehnt an: S26 Teil 2, Aufgabe 1 (Liste eindeutiger Objekte, Durchschnitt im Zeitraum) und W24 Teil 2, Aufgabe 1 (Ergebnis-Array je Streckenabschnitt)
List<T>).int[] belegung = new int[24]; Geben Sie den kleinsten und den größten gültigen Index an.belegung[24] = 5; passiert.int[][] zaehler = new int[7][24]; (Wochentag × Stunde) ist die Anzahl der Elemente anzugeben und der Zugriff auf das Element für Freitag (Index 4), Stunde 17 aufzuschreiben.Für die Planung des Serviceteams werden die Ausleihen an Station 1 ausgewertet (Anlage 4, Array daten : integer[][]).
summeJeTag(daten : integer[][]) : integer[], die für jeden Wochentag (Zeile) die Summe der Ausleihen liefert. Der Index des Ergebnis-Arrays entspricht dem Wochentag.summeJeZeitfenster(daten : integer[][]) : integer[], die für jedes Zeitfenster (Spalte) die Summe über alle Wochentage liefert. Die Funktion soll für Arrays beliebiger Größe funktionieren, d. h. keine festen Werte 7 oder 6 enthalten.Angelehnt an: S23 Teil 2, Aufgabe 2 und S22 Teil 2, Aufgabe 1 (2D-Arrays Tag × Stunde bzw. Monat × Verbraucher)
Die Werkstatt plant eine Inspektion aller Räder, die im Auswertungszeitraum genutzt wurden. Außerdem sollen defekte Räder aus der Liste der verleihbaren Räder entfernt werden. Verwenden Sie ausschließlich die Methoden der Klasse List<T> aus Anlage 2.
genutzteRaeder(ausleihen : List<Ausleihe>) : List<integer>. Sie liefert die Nummern aller Räder, die in den Ausleihen vorkommen – jede Radnummer genau einmal, in der Reihenfolge ihres ersten Auftretens.genutzteRaeder für die Testdaten aus Anlage 3 an.entferneDefekte(raeder : List<Fahrrad>) : integer. Sie entfernt alle defekten Räder mit der Methode remove aus der übergebenen Liste und gibt die Anzahl der entfernten Räder zurück.int entfernt = 0;
for (int i = 0; i < raeder.size(); i++) {
if (raeder.get(i).isDefekt()) {
raeder.remove(i);
entfernt++;
}
}
return entfernt;
Für die Liste [107, 108 (defekt), 103 (defekt), 109] bleibt danach ein defektes Rad in der Liste. Erläutern Sie die Ursache.Angelehnt an: S26 Teil 2, Aufgabe 1a (eindeutige Liste mit contains) und S22 Teil 2, Aufgabe 1 (Listen)
Die folgende Funktion soll den kleinsten Akkustand aller Räder eines Arrays liefern.
funktion minAkku(raeder : Fahrrad[]) : integer
min : integer := 0
zähle i von 1 bis raeder.length - 1
wenn raeder[i].getAkkuStand() < min dann
min := raeder[i].getAkkuStand()
Ende wenn
Ende zähle
rückgabe min
Ende funktion
Angelehnt an: W25 Teil 2, Aufgabe 3b (fehlerhafte Minimum-Funktion)
findeRad(raeder : List<Fahrrad>, radNr : integer) : Fahrrad, die das Rad mit der übergebenen Nummer liefert bzw. null, wenn es nicht in der Liste ist. Die Suche soll abbrechen, sobald das Rad gefunden wurde.vollstesRad(raeder : List<Fahrrad>, typ : string) : Fahrrad. Sie liefert von allen nicht defekten Rädern des übergebenen Typs dasjenige mit dem höchsten Akkustand. Gibt es kein solches Rad, wird null zurückgegeben.Angelehnt an: S24 Teil 1, Aufgabe 3bb (lineare Suche) und W25 Teil 2, Aufgabe 1b
In der App gibt ein Kunde an, wie viele Räder er benötigt (z. B. für eine Familie). Die App soll ihm die nächstgelegene Station anzeigen, an der mindestens so viele einsatzbereite Räder stehen (Definition „einsatzbereit“ siehe Anlage 1). Die Stationen liegen in einer Liste List<Station> stationen vor; jede Station kennt ihre Räder (getRaeder(), Anlagen 2 und 3).
naechsteStation(stationen : List<Station>, kx : double, ky : double, benoetigt : integer) : Station. Sie liefert die Station mit der kleinsten Entfernung zum Standort (kx | ky), an der mindestens benoetigt einsatzbereite Räder stehen. Gibt es keine solche Station, liefert sie null. Verwenden Sie die Funktion entfernung aus Anlage 5.benoetigt = 2 liefert. Begründen Sie, warum es nicht die nächstgelegene Station ist.Angelehnt an: W23 Teil 2, Aufgabe 2 (Nearest Neighbour) und W25 Teil 2, Aufgabe 1b
| Einheit | Anzahl Byte |
|---|---|
| 1 KiB | |
| 1 kB | |
| 1 MiB | |
| 1 GB |
Angelehnt an: W22 Teil 2, Aufgabe 2 (Dezimal → Binär)
Abgeschlossene Ausleihen werden platzsparend in einem Binärformat archiviert. Ein Datensatz besteht aus den folgenden Feldern. Zur Auswahl stehen die Java-Datentypen byte, short, int, long, float und double.
| Feld | Wertebereich | Datentyp | Byte |
|---|---|---|---|
| ausleihNr | 1 bis 3 000 000 | ||
| kundenNr | 1 bis 200 000 | ||
| radNr | 1 bis 5 000 | ||
| startStation | 1 bis 60 | ||
| startZeit | Zeitstempel in Millisekunden seit 01.01.1970 (zurzeit ca. 1,8 · 10¹²) | ||
| dauer | 0 bis 1 440 (Minuten) | ||
| strecke | Kommazahl, einfache Genauigkeit reicht aus | ||
| preisCent | 0 bis 50 000 (Preis in Cent) |
Angelehnt an: W22 Teil 2, Aufgabe 3c (Datentypen festlegen)
Bei jeder Rückgabe fotografieren zwei Kameras an der Station das Rad, damit Schäden dokumentiert werden (je Kamera ein Foto). Technische Daten:
Angelehnt an: W23 Teil 2, Aufgabe 3b (GiB) und S26 Teil 2, Aufgabe 3b (TiB)
[42, 17, 8, 33, 25] wird mit Bubblesort aufsteigend sortiert: In jedem Durchlauf werden benachbarte Elemente von links nach rechts verglichen und bei Bedarf getauscht. Das Verfahren bricht ab, sobald in einem Durchlauf nicht getauscht wurde. Tragen Sie den Inhalt des Arrays nach jedem Durchlauf und die Anzahl der Vertauschungen ein (nicht benötigte Zeilen bleiben leer).| Durchlauf | Inhalt des Arrays nach dem Durchlauf | Vertauschungen |
|---|---|---|
| 1 | ||
| 2 | ||
| 3 | ||
| 4 |
Das Serviceteam lädt die Räder in der Reihenfolge ihres Akkustands – die leersten zuerst. Die Räder liegen in einem Array raeder : Fahrrad[] vor.
sortiereNachAkku(raeder : Fahrrad[]) : void, die das Array mit Bubblesort aufsteigend nach dem Akkustand sortiert.Angelehnt an: W22 Teil 2, Aufgabe 1a (Bubblesort für Objekte)
Im Kundenportal und in der Disposition sollen Ausleihen nach unterschiedlichen Kriterien sortiert angezeigt werden. Statt für jedes Kriterium eine eigene Sortierfunktion zu schreiben, soll ein Sortieralgorithmus eine Vergleichsfunktion als Parameter erhalten.
Eine Vergleichsfunktion vergleiche(a : Ausleihe, b : Ausleihe) : integer liefert einen negativen Wert, wenn a vor b einzuordnen ist, 0, wenn a und b gleichwertig sind, und einen positiven Wert, wenn a hinter b einzuordnen ist. Sie wird wie eine normale Funktion aufgerufen: vergleiche(x, y).
sortiere(ausleihen : Ausleihe[], vergleiche : Function) : void, die das Array mit einem Sortierverfahren Ihrer Wahl unter Verwendung der übergebenen Vergleichsfunktion sortiert.nachDauerAbsteigend(a : Ausleihe, b : Ausleihe) : integer (längste Ausleihe zuerst).nachStationDannPreis(a : Ausleihe, b : Ausleihe) : integer: aufsteigend nach Startstation, bei gleicher Startstation absteigend nach Preis.nachStationDannPreis und einem stabilen Verfahren ergibt. Erläutern Sie, an welcher Stelle sich die Stabilität auswirkt.Angelehnt an: S24 Teil 2, Aufgabe 2a und W22 Teil 2, Aufgabe 1b–d
code : string := "AR-0107-8". Geben Sie die Ergebnisse der Ausdrücke an (Methoden siehe Anlage 5).| Ausdruck | Ergebnis |
|---|---|
| code.length() | |
| code.charAt(3) | |
| code.substring(3, 7) | |
| code.indexOf('-') |
equals() und nicht mit == verglichen werden sollten.Bei der Registrierung in der App muss ein Passwort folgende Regeln erfüllen:
! ? # + - _ *,pruefePasswort(pw : string) : boolean, die true liefert, wenn alle Regeln erfüllt sind, sonst false. Verwenden Sie die Hilfsfunktionen aus Anlage 5.| Passwort | Ergebnis |
|---|---|
| Rad2026! | |
| auenrad#2026 | |
| Auenrad#2026 | |
| Auen Rad#2026 |
Angelehnt an: W21 Teil 2, Aufgabe 1c (Passwortprüfung)
Die Kundendaten werden aus dem Altsystem übernommen. Dort ist der Name in einem Feld im Format „Nachname, Vorname“ gespeichert (Anlage 3, Tabelle Kunden). In Auenrad 2.0 werden Vor- und Nachname getrennt gespeichert. Außerdem trägt jedes Rad einen aufgeklebten Code, den Kunden bei einer Störung in der App eintippen. Der Code soll vor dem Absenden geprüft werden.
zerlegeName(vollname : string) : string[]. Sie liefert ein Array mit zwei Elementen: Index 0 = Vorname, Index 1 = Nachname, jeweils ohne Leerzeichen am Anfang und Ende. Enthält vollname kein Komma, werden der gesamte (bereinigte) Text als Nachname und ein leerer Vorname "" zurückgegeben."Schulze-Ebert, Anna Lena" → ["Anna Lena", "Schulze-Ebert"]; "Haas,Tom" → ["Tom", "Haas"].pruefeRadCode(code : string) : boolean. Ein gültiger Code besteht aus genau 9 Zeichen im Format AR-nnnn-p: die Zeichen „AR-“, vier Ziffern, ein Bindestrich und eine Prüfziffer p. Die Prüfziffer ist die Summe der vier Ziffern mod 10."AR-0107-8" ist gültig (0 + 1 + 0 + 7 = 8), "AR-0107-7" und "AR-01O7-8" (Buchstabe O) sind ungültig.Angelehnt an: W25 Teil 2, Aufgabe 4d (Namen per SUBSTRING aufteilen) und W21 Teil 2, Aufgabe 1c
finally-Block ausgeführt wird, und nennen Sie einen typischen Einsatzzweck.Eine Hilfsmethode der Abrechnung berechnet aus einem als Text übergebenen Betrag (in Cent) den Preis pro Minute:
static int centProMinute(String betragText, int minuten) {
int ergebnis = -1;
try {
System.out.print("A ");
if (minuten < 0) {
throw new IllegalArgumentException("Minuten negativ");
}
int betrag = Integer.parseInt(betragText); // NumberFormatException bei Nicht-Zahl
ergebnis = betrag / minuten; // Ganzzahldivision
System.out.print("B ");
} catch (NumberFormatException e) {
System.out.print("C ");
} catch (ArithmeticException e) {
System.out.print("D ");
ergebnis = 0;
} finally {
System.out.print("E ");
}
System.out.print("F ");
return ergebnis;
}
| Aufruf | Ausgabe | Rückgabewert |
|---|---|---|
| centProMinute("750", 30) | ||
| centProMinute("7,50", 30) | ||
| centProMinute("750", 0) | ||
| centProMinute("750", -5) |
catch (Exception e) { } zu verwenden. Beurteilen Sie den Vorschlag.Für den Stationsbericht soll die durchschnittliche Ausleihdauer je Station berechnet werden. Die Funktion wird von verschiedenen Programmteilen aufgerufen und soll robust gegen falsche Aufrufe und fehlende Daten sein. Für die Fehlerbehandlung stehen zur Verfügung:
IllegalArgumentException (unchecked) und die eigene, geprüfte Ausnahme KeineDatenException (checked); beide besitzen einen Konstruktor mit einer Meldung und die Methode getMessage() : string,throw new IllegalArgumentException("Text"); Schreibweise zum Behandeln: try … catch (Ausnahmetyp e) … Ende try,ausgabe(text : string) zeigt einen Text an, protokolliere(text : string) schreibt einen Eintrag in die Fehlerprotokolldatei.durchschnittsDauer(ausleihen : List<Ausleihe>, stationNr : integer) : double auftreten können.durchschnittsDauer. Sie liefert die durchschnittliche Dauer (in Minuten, als Kommazahl) aller Ausleihen, die an der Station stationNr begonnen haben. Ist ausleihen gleich null oder stationNr kleiner als 1, wird eine IllegalArgumentException ausgelöst. Gibt es keine Ausleihe für diese Station, wird eine KeineDatenException ausgelöst.zeigeStationsbericht(ausleihen : List<Ausleihe>, stationNr : integer) : void. Sie ruft durchschnittsDauer auf und gibt das Ergebnis aus. Bei einer KeineDatenException soll der Text „Für diese Station liegen keine Ausleihen vor.“ ausgegeben werden, bei einer IllegalArgumentException wird die Meldung der Ausnahme protokolliert.Angelehnt an: W25 Teil 2, Aufgabe 1 und W21 Teil 2, Aufgabe 2 (Fehlerfall bzw. null behandeln)
Die folgende Funktion soll eine nicht negative ganze Zahl n als Binärzahl (Zeichenkette) liefern. Der Operator + verkettet Zeichenketten; Zahlen werden dabei in Text umgewandelt.
funktion binaer(n : integer) : string
wenn n = 0 dann
rückgabe ""
Ende wenn
rückgabe binaer(n div 2) + (n mod 2)
Ende funktion
binaer(13) durch. Tragen Sie jeden Aufruf (auch die rekursiven) in eine Zeile der Tabelle ein. Nicht benötigte Zeilen bleiben leer.| Aufruf | n | n div 2 | n mod 2 | Rückgabewert |
|---|---|---|---|---|
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 |
Angelehnt an: W22 Teil 2, Aufgabe 2a (Schreibtischtest und Korrektur einer rekursiven Umwandlung)
Die Radnummern des gesamten Bestands werden aufsteigend sortiert in einem Array nummern : integer[] gespeichert. Für die Suche nach einer Radnummer soll die binäre Suche rekursiv umgesetzt werden: Das mittlere Element des Suchbereichs (mitte := (links + rechts) div 2) wird mit dem gesuchten Wert verglichen. Ist es nicht der gesuchte Wert, wird nur noch in der linken bzw. rechten Hälfte weitergesucht.
suche(nummern : integer[], gesucht : integer, links : integer, rechts : integer) : integer, die den Index von gesucht liefert bzw. −1, wenn der Wert nicht enthalten ist. Der erste Aufruf lautet suche(nummern, gesucht, 0, nummern.length - 1).nummern = [101, 102, 103, …, 112] (12 Elemente, lückenlos). Geben Sie für gesucht = 112 alle Aufrufe von suche mit den Werten von links, rechts und mitte an.Angelehnt an: W22 Teil 2, Aufgabe 2b (Rekursion vs. Iteration)
An Station 3 ist nur noch ein Lastenrad frei. Jana Lorenz und Tom Haas tippen fast gleichzeitig in der App auf „Reservieren“. Der Server verarbeitet beide Anfragen in parallelen Threads. Jeder Thread führt aus: (1) Anzahl freier Lastenräder aus der Datenbank lesen, (2) prüfen, ob die Anzahl größer als 0 ist, (3) die gelesene Anzahl um 1 verringert zurückschreiben und eine Reservierung speichern. Es ergibt sich folgender Ablauf:
| Zeit | Thread A (Jana) | Thread B (Tom) | freieLastenraeder in der DB |
|---|---|---|---|
| t1 | liest freieLastenraeder → 1 | 1 | |
| t2 | liest freieLastenraeder → 1 | 1 | |
| t3 | prüft 1 > 0 → ja | 1 | |
| t4 | prüft 1 > 0 → ja | 1 | |
| t5 | schreibt 1 − 1 = 0, speichert Reservierung | 0 | |
| t6 | schreibt 1 − 1 = 0, speichert Reservierung | 0 |
Angelehnt an: W21 Teil 2, Aufgabe 1b (Blockieren von Terminen bei gleichzeitigen Buchungen)
Lastenräder sollen künftig über die App im Voraus reserviert werden können. Das Entwicklungsteam diskutiert zwei Varianten:
| Variante | Vorteil | Nachteil |
|---|---|---|
| A | ||
| B |
reserviere(db : RadDB, radNr : integer, kundenNr : integer) : boolean für Variante B. Sie liefert true, wenn die Reservierung gespeichert wurde, und false, wenn das Rad nicht frei ist oder ein anderer Kunde schneller war.Angelehnt an: W21 Teil 2, Aufgabe 1b
Diese echten Prüfungsaufgaben passen zu diesem Block (Dateien im Ordner Prüfungsvorbereitung). Lösungen gibt es nur für 2023 Sommer, 2023 Winter, 2024 Sommer und 2025 Winter (dort vom u-form-Verlag).
| Prüfung | Aufgabe | Thema | Datei · Lösung |
|---|---|---|---|
| Winter 2021 | T2 A2 (25 P) | Pseudocode im 2D-Terminplan (Suche) | 2021/winter/AE 2021 Winter Teil 2.pdf · keine Lösung |
| Winter 2021 | T2 A1b (4 P), A1c (6 P) | Nebenläufigkeit (Termine blockieren); Passwortprüfung im Struktogramm, Fehler finden | wie oben · keine Lösung |
| Sommer 2022 | T2 A1 (30 P) | Statistik aus 2D-Array, Min/Max, Liste | 2022/sommer/AE 2022 Sommer Teil 2.pdf · keine Lösung |
| Sommer 2022 | T1 A3cb (4 P) | notifyObservers in Pseudocode (Liste durchlaufen) | 2022/sommer/AE 2022 Sommer Teil 1.pdf · keine Lösung |
| Winter 2022 | T2 A1 (25 P) | Bubblesort, Vergleichsfunktion, generisches Sortieren | 2022/winter/AE 2022 Winter Teil 2.pdf · keine Lösung |
| Winter 2022 | T2 A2 (25 P), A3c (3 P) | Rekursion Dezimal → Binär, Schreibtischtest, iterative Variante; Datentypen | wie oben · keine Lösung |
| Sommer 2023 | T2 A2 (25 P) | 2D-Array Besucher Tag × Stunde | 2023/sommer/AE Sommer 2023 Teil 2.pdf · Lösung: AE Sommer 2023 Teil 1+2 Lösungen.pdf |
| Sommer 2023 | T1 A2bc, A2bd (9 P) | Methoden zu einem Klassendiagramm in Pseudocode | 2023/sommer/AE Sommer 2023 Teil 1.pdf · Lösung wie oben |
| Winter 2023 | T2 A2 (25 P) | Nearest Neighbour (Minimumsuche im Objekt-Array) | 2023/winter/AE Winter 2023 Teil 2.pdf · Lösung: AE Winter 2023 Teil 2 Lösungen.pdf |
| Winter 2023 | T2 A3b (7 P) | Speicherbedarf in GiB | wie oben |
| Sommer 2024 | T1 A3b (15 P) | Methoden mit Listen, lineare Suche | 2024/sommer/AE Sommer 2024 Teil 1+2+3.pdf · Lösung: AE Sommer 2024 Teil 1 Lösungen.pdf |
| Sommer 2024 | T2 A2 (20 P) | Sortieren mit Vergleichsfunktion, Zählfunktion | wie oben · Lösung: AE Sommer 2024 Teil 2 Lösungen.pdf |
| Winter 2024 | T1 A2c (8 P) | Skript: Subnetz durchlaufen, JSON-Ausgabe | 2024/winter/AE Winter 2024 Teil 1.pdf · keine Lösung |
| Winter 2024 | T2 A1 (25 P), A2 (25 P) | Verspätungen je Strecke zählen (Ergebnis-Array); HTTP-Antwort bauen, nächste Abfahrten filtern | 2024/winter/AE Winter 2024 Teil 2.pdf · keine Lösung |
| Winter 2025 | T2 A1 (30 P) | freie Tische filtern (verschachtelte Listen), bester Tisch (Minimum) | 2025/winter/AE Winter 2025 Teil 2.pdf · Lösung: AE Winter 2025 Lösungserleuterungen.pdf (u-form) |
| Winter 2025 | T2 A3b (14 P), A4d (8 P) | fehlerhafte Minimum-Funktion; Namen per SUBSTRING aufteilen | wie oben |
| Sommer 2026 | T2 A1 (30 P), A2ba (4 P) | Liste eindeutiger Objekte, Durchschnitt im Zeitraum; Liste filtern | 2026/sommer/AE Sommer 2026 Teil 2.pdf · keine Lösung |
| Sommer 2026 | T2 A3b (5 P) | Speicherbedarf in TiB | wie oben |