Block 02 · Algorithmen & Programmierung

Immer 9/9 Prüfungen · Ø 42 von 200 Punkten · vor allem Teil 2 (dort immer eine 25–30-Punkte-Algorithmusaufgabe), in mehreren Prüfungen auch Teil 1 · Lösungen: 02_Algorithmen_Loesungen.html
So arbeitest du mit diesem Block Erst den Erklärungssatz lesen, dann die Aufgaben mit dem Belegsatz daneben lösen (Belegsatz am besten separat ausdrucken und neben die Aufgaben legen). Zeitrichtwert: 1 Punkt ≈ 0,9 Minuten. Lösungen erst danach in der separaten Lösungsdatei nachsehen. Zu jeder Code-Aufgabe gibt es dort Pseudocode und eine lauffähige Java-Datei im Ordner java\02_Algorithmen\.

A · Erklärungssatz

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.

UnterthemaKategorieTypische PunkteWichtigste Fähigkeit
ALG-01 Algorithmus in PseudocodeImmer25–30 (T2), 4–15 (T1)Grundmuster sicher anwenden, sauber initialisieren
ALG-02 Arrays und ListenImmerin ALG-01 enthaltenIndexgrenzen, 2D-Arrays, List<T>-Methoden
ALG-03 Minimum/Maximum, SucheOft10–25richtig initialisieren, Objekt statt Wert merken
ALG-04 Speicherbedarf, ZahlensystemeWahrscheinlich3–71024er-Präfixe, Bit/Byte, Rechenweg mit Einheiten
ALG-05 SortierenWahrscheinlich10–25Bubblesort, Vergleichsfunktion
ALG-06 ZeichenkettenWahrscheinlich5–8Zeichen prüfen, Text zerlegen
ALG-07 ExceptionsWahrscheinlich2–6Fehlerfälle erkennen und behandeln
ALG-08 RekursionSonstigebis 12Basisfall, Schreibtischtest
ALG-09 NebenläufigkeitSonstige4Race Condition, Sperren

ALG-01 · Algorithmus in Pseudocode schreiben Immer

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)

So sieht die Aufgabe aus

Pseudocode-Bausteine (IHK-Stil) und ihre Java-Entsprechung

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:

BausteinPseudocodeJava
Variable anlegenanzahl : integer := 0int anzahl = 0;
Zuweisungsumme := summe + xsumme = summe + x;
Vergleich= <> < > <= >=== != < > <= >=
Logikund oder nicht&& || !
Verzweigungwenn … dann … sonst … Ende wennif (…) { … } else { … }
Zählschleifezähle i von 0 bis n - 1 … Ende zählefor (int i = 0; i <= n - 1; i++)
Kopfgesteuertsolange … Ende solangewhile (…) { … }
Fußgesteuertwiederhole … solange …do { … } while (…);
Ganzzahldivision / Resta div b, a mod ba / b (bei int), a % b
Objekt erzeugennew Artikel(…), new List<Artikel>()new Artikel(…), new ArrayList<>()
Array anlegen / Längenew integer[5], feld.lengthnew int[5], feld.length
Methode / Rückgabefunktion name(p : typ) : typ … rückgabe x … Ende funktiontyp name(typ p) { … return x; }
„kein Objekt“nullnull
MerkeBewertet wird die Logik: richtige Schleife, richtige Bedingung, richtige Initialisierung, Rückgabe. Ein fehlendes Semikolon oder 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.

Die fünf Grundmuster

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
uebersicht (außen, i) i = 0: Lieferant A i = 1: Lieferant B i = 2: Lieferant C bestellungen (innen, j) j = 0: A · 120,00 € j = 1: B · 80,00 € j = 2: A · 45,50 € j = 3: C · 300,00 € j = 4: A · 19,90 € durchgezogen: Bedingung erfüllt, wird addiert gestrichelt: geprüft, aber nicht addiert i = 0: summe := 120,00 + 45,50 + 19,90 = 185,40 → danach i = 1, summe wieder 0
Abb. A-1: Verschachtelte Schleifen – für jedes Element außen wird die innere Liste komplett durchlaufen (n × m Prüfungen)

So gehst du vor

  1. Signatur lesen: Was kommt hinein (Parameter), was muss heraus? integer → Zähler oder Summe, double → Durchschnitt, List → Filter, Array → „je Index“, Objekt → Suche/Minimum, void → vorhandene Objekte per Setter ändern.
  2. Beispieldaten von Hand durchrechnen. Wenn du das erwartete Ergebnis der Beispieltabelle nicht selbst ausrechnen kannst, hast du die Aufgabe noch nicht verstanden.
  3. Muster wählen (eins oder mehrere der fünf oben, dazu Minimum/Maximum aus ALG-03).
  4. Variablen vor der Schleife anlegen und initialisieren. Bei verschachtelten Schleifen: Was gilt für alle Durchläufe (außen anlegen), was nur für ein äußeres Element (innen zurücksetzen)?
  5. Schleife über alle Elemente: zähle i von 0 bis liste.size() - 1 bzw. bis feld.length - 1.
  6. Bedingung mit Gettern formulieren. Jede Bedingung aus dem Aufgabentext einzeln abhaken („nicht defekt“, „im Zeitraum“, „mehr als …“: > oder >=?).
  7. Nach der Schleife: Sonderfälle (Division durch 0, nichts gefunden → null), dann rückgabe.
  8. Schreibtischtest mit zwei, drei Datensätzen aus der Tabelle der Aufgabe.
Typische Fehler
So wird es in der Prüfung gefragt

ALG-02 · Arrays und Listen durchlaufen Immer

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)

Eindimensionales Array

Zweidimensionales Array (Tabelle)

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.

[0] [1] [2] [3] 14 Uhr 17 Uhr 20 Uhr 23 Uhr Zeilensumme [0] Saal 1 [1] Saal 2 [2] Saal 3 Spaltensumme 458012060 305540 12257033 90 305215140 87160280133 660 besucher[1][2] = 90 → Zeile 1 (Saal 2), Spalte 2 (20 Uhr) besucher.length = 3 (Zeilen) · besucher[0].length = 4 (Spalten) · 12 Elemente
Abb. A-2: 2D-Array besucher[saal][vorstellung] mit Zeilen- und Spaltensummen

Zeilensummen (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.

Listen: 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>.

MethodeWirkungBeispiel
add(t : T) : voidhängt t hinten anergebnis.add(a)
get(i : integer) : Tliefert das Element an Position i (ab 0)artikel.get(0)
size() : integerAnzahl der Elementezähle i von 0 bis artikel.size() - 1
contains(t : T) : booleantrue, wenn ein gleiches Element enthalten istwenn nicht namen.contains(n) dann …
remove(i : integer) : voidentfernt Element i, alle folgenden rücken eine Position nach vornliste.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.

vorher i = 1, remove(1) [0] Milch [1] Brot ✗ [2] Käse ✗ [3] Tee ↑ i = 1 nachher i := i + 1 = 2 [0] Milch [1] Käse ✗ [2] Tee ↑ i = 2 „Käse“ rückt auf [1] und wird übersprungen
Abb. A-3: Index-Verschiebung nach 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

Array oder Liste?

KriteriumArrayListe (List<T>)
Größefest beim Anlegendynamisch, wächst mit add
Anzahl Elementefeld.length (Attribut)liste.size() (Methode)
Zugrifffeld[i]liste.get(i)
Einfügen/Löschennur durch Umkopierenadd, remove eingebaut
Elementtypen (Java)auch primitive Typen (int[])nur Objekte (List<Integer>)
Geeignet fürfeste Struktur: 24 Stunden, 12 Monate, 7 Tageunbekannte Anzahl: Ergebnislisten, Filter
Typische Fehler
So wird es in der Prüfung gefragt

ALG-03 · Minimum/Maximum, lineare Suche Oft

Vorkommen: 4/9 · Prüfungen: W21, W23, S24, W25 (dazu Min/Max in S22 innerhalb der 2D-Aufgabe) · Ø 21 Punkte

Minimum und Maximum

Prinzip: Man merkt sich den „bisher besten“ Wert und vergleicht jedes weitere Element damit. Entscheidend ist der Startwert.

StartwertSo geht'sGeeignet, wenn …Risiko
erstes Elementmin := feld[0]…, Schleife ab Index 1alle Elemente zählen und mindestens eins da istleere Liste/Array → Laufzeitfehler
null bzw. Merkerbestes := null, Vergleich wenn bestes = null oder …nur Elemente mit Bedingung zählen (z. B. nur freie)Aufrufer muss null behandeln
Extremwertmin := 999999 bzw. Integer.MAX_VALUEObergrenze der Werte ist sicher bekanntwirkt geraten; falscher Wert, wenn nichts passt
0 (!)min := 0nie für ein Minimum positiver WerteErgebnis 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).

Typische Falle: Initialisierung mit 0 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.

Lineare Suche

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.

Nächster Nachbar (Minimum einer berechneten Größe)

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₁)²)

012345678 123456 x (km) y (km) Fahrgast F (3|2) T1 (1|5) frei · 3,61 T2 (4|3) besetzt · 1,41 T3 (6|1) frei · 3,16 ✓ T4 (7|5) frei · 5,00
Abb. A-4: Nächstes freies Taxi – T2 ist am nächsten, kommt aber wegen der Bedingung nicht in Frage; Ergebnis T3
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

Beste Übereinstimmung („kleinste passende Differenz“)

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).

Typische Fehler
So wird es in der Prüfung gefragt

ALG-04 · Speicherbedarf berechnen / Zahlensysteme Wahrscheinlich

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

Bit, Byte und die zwei Arten von Präfixen

SI (dezimal)ByteIEC (binär)ByteUnterschied
1 kB (Kilobyte)10³ = 1 0001 KiB (Kibibyte)2¹⁰ = 1 0242,4 %
1 MB (Megabyte)10⁶ = 1 000 0001 MiB (Mebibyte)2²⁰ = 1 048 5764,9 %
1 GB (Gigabyte)10⁹1 GiB (Gibibyte)2³⁰ = 1 073 741 8247,4 %
1 TB (Terabyte)10¹²1 TiB (Tebibyte)2⁴⁰ = 1 099 511 627 77610,0 %
MerkeVon einer Einheit zur nächsten: bei SI ÷ 1000, bei IEC ÷ 1024. Bit → Byte immer ÷ 8. Wird in der Aufgabe GiB/TiB verlangt, rechnest du durchgehend mit 1024. Eine Festplatte „8 TB“ hat 8 · 10¹² Byte = 7,28 TiB.

Zahlensysteme umrechnen

Dezimal0123456789101112131415
Binär0000000100100011010001010110011110001001101010111100110111101111
Hex0123456789ABCDEF

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.

Datentypen und ihr Speicherbedarf

Java-TypGrößeWertebereich / Genauigkeittypischer EinsatzSQL-Gegenstück
booleanlogisch 1 Bit (JVM meist 1 Byte)true / falseMerker, StatusBOOLEAN / TINYINT(1)
byte1 Byte−128 … 127kleine Zähler, ProzentTINYINT
short2 Byte−32 768 … 32 767Jahreszahl, Minuten eines TagesSMALLINT
int4 Byteca. ±2,1 MilliardenIDs, Mengen, Cent-BeträgeINT
long8 Byteca. ±9,2 · 10¹⁸Zeitstempel in ms, große SummenBIGINT
float4 Byteca. 7 signifikante StellenMesswerte, wenn Genauigkeit reichtFLOAT
double8 Byteca. 15–16 signifikante StellenBerechnungen, DurchschnitteDOUBLE
char2 Byteein UTF-16-Zeicheneinzelnes ZeichenCHAR(1)
StringvariabelJava intern ca. 1–2 Byte je ZeichenTexteVARCHAR(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.

Speicherbedarf von Bildern, Video und Audio

MediumFormel (unkomprimiert)
BildBreite × Höhe (Pixel) × Farbtiefe (Bit/Pixel) ÷ 8 = Byte. Farbtiefe = Kanäle × Bit je Kanal (RGB mit 8 Bit je Kanal = 24 Bit)
VideoBildgröße (Byte) × Bilder pro Sekunde (fps) × Sekunden
AudioAbtastrate (Hz) × Bittiefe (Bit) × Kanäle × Sekunden ÷ 8
Komprimiertunkomprimierte 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.

So gehst du vor

  1. Alle Angaben mit Einheit herausschreiben (Pixel, Bit/Kanal, Kanäle, Bilder/Tag, Tage/Jahr, Kompression).
  2. Größe eines Elements in Bit berechnen, dann ÷ 8 in Byte.
  3. Hochrechnen (× Anzahl × Zeitraum). Wochen: 52, Jahr: 365 Tage, sofern nichts anderes angegeben ist.
  4. In die verlangte Einheit umrechnen: je Stufe ÷ 1024 (bzw. ÷ 1000 bei SI).
  5. Runden wie verlangt; Speicherbedarf und Anzahl Datenträger aufrunden.
  6. Antwortsatz mit Einheit. Jede Zeile des Rechenwegs mit Einheiten zeigt dem Prüfer, dass du verstanden hast. Das bringt Teilpunkte, auch wenn sich ein Rechenfehler einschleicht.
Typische Fehler
So wird es in der Prüfung gefragt

ALG-05 · Sortieren Wahrscheinlich

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

Bubblesort

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
Start nach Durchlauf 1 nach Durchlauf 2 nach Durchlauf 3 29 11 37 4 29↔11 tauschen · 29/37 ok · 37↔4 tauschen 11 29 4 37 11/29 ok · 29↔4 tauschen 11 4 29 37 11↔4 tauschen 4 11 29 37 sortiert grau/fett = Element steht an seiner endgültigen Position
Abb. A-5: Bubblesort auf [29, 11, 37, 4] – nach jedem Durchlauf ist ein weiteres Element fertig

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 und Insertionsort (zum Verständnis)

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
VerfahrenIdeebester Fallschlechtester Fallstabil?
BubblesortNachbarn tauschenO(n) mit AbbruchO(n²)ja
SelectionsortMinimum nach vorneO(n²)O(n²)nein (Tausch über Distanz)
InsertionsorteinsortierenO(n)O(n²)ja
Mergesorthalbieren, sortieren, zusammenmischenO(n log n)O(n log n)ja (braucht Zusatzspeicher)
QuicksortPivot wählen, aufteilenO(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.

Objekte sortieren und die Vergleichsfunktion

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ückgabeBedeutungSortieralgorithmus
negativ (z. B. −1)a gehört vor bnicht tauschen
0gleichwertignicht tauschen (Stabilität)
positiv (z. B. 1)a gehört hinter btauschen: 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.

Typische Fehler
So wird es in der Prüfung gefragt

ALG-06 · Zeichenketten verarbeiten Wahrscheinlich

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.

1 4 . 0 9 . 2 0 2 6 0123456789 substring(3, 5) = "09" (Index 3 bis 4, 5 ist ausgeschlossen) indexOf('.') = 2 length() = 10, charAt(9) = '6' d =
Abb. A-6: String d = "14.09.2026" mit Indizes
Methode (Java, im Pseudocode genauso)WirkungBeispiel mit d = "14.09.2026"
length()Anzahl Zeichen10
charAt(i)Zeichen an Position icharAt(0) = '1'
substring(von, bis)Teilstring von von bis bis − 1substring(6, 10) = "2026"
indexOf(x)erste Position von x, sonst −1indexOf('-') = −1
split(trenner)zerlegt in ein String-Array3 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)Inhaltsvergleichd.equals("14.09.2026") = true
compareTo(t)alphabetischer Vergleich (<0, 0, >0)"Abel".compareTo("Berg") < 0
startsWith, endsWith, containsTeiltext prüfend.endsWith("2026") = true
toUpperCase(), replace(a, b)umwandeln, ersetzend.replace('.', '/') = "14/09/2026"
Character.isDigit(c), isLetter, isUpperCase, isLowerCaseZeichenklasse prüfenisDigit('4') = true
c - '0', Integer.parseInt(s)Ziffer bzw. Text in Zahl'7' - '0' = 7, parseInt("2026") = 2026

Muster 1: Zeichen für Zeichen prüfen

Zwei Varianten, die man auseinanderhalten muss:

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

Muster 2: Text zerlegen

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.

Strings vergleichen

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.

Typische Fehler
So wird es in der Prüfung gefragt

ALG-07 · Exceptions / Fehlerbehandlung Wahrscheinlich

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
try-Block ausführen Ausnahme? [nein] try-Block läuft bis zum Ende [ja] Rest des try-Blocks wird übersprungen passender catch? [ja] catch-Block ausführen [nein] finally-Block weiter nach try/catch/finally finally-Block, danach Ausnahme an Aufrufer weiter
Abb. A-7: Ablauf von try/catch/finally – der finally-Block läuft in jedem Fall (auch bei return im try-Block)

Checked und unchecked Exceptions (Java)

checked (geprüft)unchecked (ungeprüft)
OberklasseException (aber nicht RuntimeException)RuntimeException (und Error)
Compiler verlangt Behandlung?ja: fangen (try/catch) oder mit throws in der Methodensignatur weiterreichennein
Ursache typischerweiseäußere Umstände, die auch korrekter Code nicht verhindern kannProgrammierfehler, die man durch Prüfungen vermeiden kann
BeispieleIOException, FileNotFoundException, SQLException, eigene fachliche AusnahmenNullPointerException, 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;
}

Typische Fehlerfälle in Prüfungsalgorithmen

Fehlerfallerkennensinnvolle Reaktion
Parameter ist nullwenn liste = null dannIllegalArgumentException werfen (sonst später NullPointerException)
leere Liste / nichts passtsize() = 0, Zähler = 0dokumentierten Sonderwert zurückgeben (null, 0.0, leere Liste) oder eigene Exception
Division durch 0Nenner vorher prüfenbei 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 MethodenanfangIllegalArgumentException
ungültiger Indexi < 0 oder i >= size()vorher prüfen; sonst IndexOutOfBoundsException
Text ist keine ZahlInteger.parseIntNumberFormatException fangen, Benutzer informieren
Datei, Datenbank, Netzwerk nicht erreichbarchecked Exceptionsfangen: 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.

Typische Fehler
So wird es in der Prüfung gefragt

ALG-08 · Rekursion Sonstige

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
Abstieg: Aufrufe Aufstieg: Rückgaben fakultaet(4) = 4 * fakultaet(3) fakultaet(3) = 3 * fakultaet(2) fakultaet(2) = 2 * fakultaet(1) fakultaet(1) = 1 (Basisfall) liefert 1 liefert 2 · 1 = 2 liefert 3 · 2 = 6 liefert 4 · 6 = 24
Abb. A-8: Aufrufstapel von fakultaet(4) – erst bis zum Basisfall absteigen, dann die Ergebnisse rückwärts zusammensetzen

Schreibtischtest einer Rekursion: Eine Zeile je Aufruf; erst alle Aufrufe nach unten notieren, dann die Rückgabewerte von unten nach oben eintragen. Beispiel quersumme(4721):

Aufrufnn mod 10n div 10Rückgabe
1472114721 + 13 = 14
24722472 + 11 = 13
347747 + 4 = 11
44Basisfall (n < 10)4
RekursionIteration (Schleife)
Lesbarkeitoft 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
Speicherjeder Aufruf belegt Platz auf dem Stack; sehr tiefe Rekursion → StackOverflowErrorkonstanter Speicher
LaufzeitOverhead durch Funktionsaufrufe; naive Varianten rechnen Teilprobleme mehrfach (Fibonacci)meist schneller
Fehlersucheschwerer zu debuggeneinfacher

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.

Typische FehlerBasisfall fehlt oder wird nie erreicht (Parameter wird nicht kleiner, z. B. 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.
So wird es in der Prüfung gefragtW22 T2 A2: gegebene rekursive Funktion (Dezimal → Binär) per Schreibtischtest durchspielen, einen Fehler für einen Randfall finden und korrigieren, Mindestzahl Testfälle für Anweisungsüberdeckung, je einen Vor- und Nachteil von Rekursion vs. Iteration nennen, iterative Lösung mit einer Liste schreiben.

ALG-09 · Nebenläufigkeit Sonstige

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.

BegriffBedeutung
gemeinsame RessourceDaten, auf die mehrere Abläufe zugreifen (Platzstatus, Kontostand, Lagerbestand)
kritischer AbschnittCodebereich, in dem die Ressource gelesen und abhängig davon verändert wird; darf nicht von zwei Abläufen gleichzeitig durchlaufen werden
Race ConditionErgebnis hängt von der zufälligen zeitlichen Reihenfolge („Wettlauf“) der Abläufe ab
Lost Updateeine Änderung überschreibt eine andere, die dazwischen passiert ist; Folge z. B. Doppelbuchung
Deadlockzwei Abläufe warten gegenseitig auf eine Sperre des anderen – beide hängen
Kasse A Datenbank Kasse B t1t2t3t4t5t6 lies Platz 7/12 „frei“ lies Platz 7/12 „frei“ frei → verkaufen frei → verkaufen setze „verkauft an A“ setze „verkauft an B“ → Platz zweimal verkauft, A’s Buchung überschrieben
Abb. A-9: Race Condition beim Kartenverkauf – beide lesen „frei“, bevor einer schreibt

Lösungen: Sperren und atomare Operationen

Pessimistisches SperrenOptimistisches Sperren
AnnahmeKonflikte sind wahrscheinlichKonflikte sind selten
AblaufDatensatz 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.
VorteilKonflikt kann gar nicht entstehen; einfach zu verstehenkeine Wartezeiten, gute Parallelität, keine Deadlocks
NachteilWartezeiten, schlechtere Skalierung, Deadlock-Gefahr; zu lange gehaltene Sperren blockieren andereKonflikt fällt erst beim Speichern auf → Vorgang muss wiederholt oder abgelehnt werden
Typische FehlerVerfügbarkeit nur in der App (Client) prüfen statt auf dem Server bzw. in der Datenbank; „lesen – prüfen – schreiben“ in getrennten Schritten ohne Sperre; Sperre über die ganze Bedienzeit des Nutzers halten; ganze Tabelle statt einzelner Zeile sperren.
So wird es in der Prüfung gefragtW21 T2 A1b (4 P): Für eine Online-Terminvergabe sind zwei Abläufe beschrieben: Variante 1 blockiert erst den vom Patienten gewählten Termin (und meldet, falls er inzwischen weg ist), Variante 2 blockiert schon während der Auswahl alle freien Termine eines ganzen Monats. Eine Variante ist im Hinblick auf die Praxistauglichkeit auszuwählen und zu begründen. Erwartet wird eine Abwägung: Doppelbuchungen verhindern, aber andere Nutzer nicht unnötig lange und umfangreich blockieren (Sperrumfang, Sperrdauer, Freigabe).

B · Belegsatz

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, prozedurale Programmierung

PseudocodeBeschreibung
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

Anlage 1 · Ausgangssituation

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.

Anlage 2 · Klassen der Plattform „Auenrad 2.0“ (Auszug)

Station - stationNr : integer - name : string - x : double - y : double - kapazitaet : integer - raeder : List<Fahrrad> + getRaeder() : List<Fahrrad> Fahrrad - radNr : integer - typ : string - akkuStand : integer - kmStand : double - defekt : boolean + isDefekt() : boolean 0..1 raeder * List<T> + add(t : T) : void + get(i : integer) : T + size() : integer + contains(t : T) : boolean + remove(i : integer) : void Ausleihe - ausleihNr : integer - kundenNr : integer - radNr : integer - startStation : integer - zielStation : integer - datum : Datum - startMinute : integer - dauer : integer - strecke : double - preis : double … KundenStatistik - kundenNr : integer - anzahl : integer - gesamtDauer : integer - durchschnitt : double + KundenStatistik(kundenNr : integer) Kunde - kundenNr : integer - name : string - tarif : string … Datum + liegtZwischen(von : Datum, bis : Datum) : boolean + toString() : string
Anlage 2: Klassendiagramm (Auszug)

Methoden der Klasse List<T>:

Anlage 3 · Testdaten (Stand: 18.09.2026, 06:00 Uhr, nach der nächtlichen Umverteilung)

Listen und Arrays enthalten die Objekte in der Reihenfolge der Tabellenzeilen.

Stationen (List<Station> stationen)
stationNrnamexykapazitaetraeder (Radnummern)
1Hauptbahnhof2.01.020101, 102, 103
2Marktplatz3.52.512104, 105
3Uni-Campus6.04.016106, 107, 108
4Hafen1.05.510109
5Klinikum5.07.08110
6Seepark8.51.510111, 112
Fahrräder (List<Fahrrad> raeder – alle Räder)
radNrtypakkuStandkmStanddefektsteht an Station
101E-Bike851234.5false1
102E-Bike152210.0false1
103Lastenrad60540.2true1
104E-Bike22980.7false2
105E-Bike95310.0false2
106Lastenrad401502.3false3
107E-Bike83120.9false3
108E-Bike71875.4true3
109E-Bike55455.0false4
110Lastenrad28760.8false5
111E-Bike90120.3false6
112E-Bike331999.9false6
Kunden
kundenNrname (Altsystem)tarif
5001Yilmaz, DenizAbo
5002Lorenz, JanaBasis
5003Nowak, PiotrStudent
5004Schulze-Ebert, Anna LenaAbo
5005Haas, TomBasis
Ausleihen vom 14.09. bis 17.09.2026 (List<Ausleihe> ausleihen)
ausleihNrkundenNrradNrstartStationzielStationdatumstartMinutedauerstreckepreis
15001101132026-09-14462184.60.00
25002105212026-09-14485122.13.40
35003106352026-09-14750759.87.50
45001104312026-09-141030224.90.00
55004110542026-09-155559512.46.50
65005109422026-09-151008315.07.20
75002111632026-09-151055648.713.80
85003107332026-09-16785406.24.00
95001101162026-09-16475267.30.00
105004112612026-09-161100337.10.30
115005102122026-09-1749291.92.80
125003106532026-09-17640589.15.80

Anlage 4 · Ausleihen an Station 1 (Hauptbahnhof) in KW 38/2026

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] Mo114911184
[1] Di016810213
[2] Mi215119195
[3] Do0131012226
[4] Fr3121215179
[5] Sa5214201611
[6] So611018137

Anlage 5 · Operatoren und Hilfsfunktionen

Ausdruck / FunktionErgebnis
a div bganzzahliger Anteil der Division zweier integer-Werte (17 div 5 = 3)
a mod bRest der ganzzahligen Division (17 mod 5 = 2)
und, oder, nichtlogische Verknüpfungen
wurzel(x : double) : doubleQuadratwurzel von x
abs(x : double) : doubleBetrag von x
entfernung(x1 : double, y1 : double, x2 : double, y2 : double) : doubleLuftlinie zwischen (x1|y1) und (x2|y2) in km: √((x2 − x1)² + (y2 − y1)²)
feld.lengthAnzahl 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() : integerAnzahl der Zeichen der Zeichenkette s
s.charAt(i : integer) : charZeichen an Position i (erstes Zeichen: Position 0)
s.substring(von : integer, bis : integer) : stringTeilzeichenkette von Position von bis Position bis − 1
s.indexOf(c : char) : integererste Position des Zeichens c in s; −1, wenn c nicht vorkommt
s.trim() : strings ohne Leerzeichen am Anfang und am Ende
istGrossbuchstabe(c : char) : booleantrue für A–Z, Ä, Ö, Ü
istKleinbuchstabe(c : char) : booleantrue für a–z, ä, ö, ü, ß
istBuchstabe(c : char) : booleantrue für Groß- und Kleinbuchstaben
istZiffer(c : char) : booleantrue für 0–9
zahlwert(c : char) : integerZahlenwert einer Ziffer, z. B. zahlwert('7') = 7
+ bei Zeichenkettenverkettet; 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.

Anlage 6 · Datenbankzugriff für die Reservierung von Lastenrädern

RadDB + leseRad(radNr : integer) : RadInfo + setzeStatusWennVersion(radNr : integer, neuerStatus : string, erwarteteVersion : integer) : boolean + legeReservierungAn(radNr : integer, kundenNr : integer) : void RadInfo - status : string - version : integer + getStatus() : string + getVersion() : integer
Anlage 6: Klassen für den Datenbankzugriff (Auszug)

C · Aufgaben

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).

ALG-01 · Algorithmus in Pseudocode schreiben

Aufgabe ALG-01.1 ★☆☆ Grundlagen 6 Punkte

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
5 Pa) Ergänzen Sie die Lücken (1) bis (5).
LückeInhalt
(1)
(2)
(3)
(4)
(5)
1 Pb) Geben Sie an, welchen Wert die Funktion für die Liste aller Räder aus Anlage 3 zurückgibt, und nennen Sie die betroffenen Radnummern.

Angelehnt an: S24 Teil 2, Aufgabe 2b (Zählfunktion über ein Array)

Aufgabe ALG-01.2 ★★☆ Anwendung 10 Punkte

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.

5 Pa) Entwickeln Sie eine Funktion 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.
5 Pb) Entwickeln Sie eine Funktion 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

Aufgabe ALG-01.3 ★★★ Prüfungsniveau 30 Punkte

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.

6 Pa) Entwickeln Sie eine Funktion 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.
Für die Testdaten und anzahlStationen = 6 ergibt sich: [0, 0, 1, 0, 1, 1].
10 Pb) Entwickeln Sie eine Funktion 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.
ausleihen (Auszug)
ausleihNrkundenNr…
15001…
25002…
35003…
45001…
55004…
………
Ergebnis: List<KundenStatistik>
kundenNranzahlgesamtDauerdurchschnitt
5001000.0
5002000.0
5003000.0
5004000.0
5005000.0
14 Pc) Entwickeln Sie eine Funktion 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.
Beispiel für von = 16.09.2026 und bis = 17.09.2026:
kundenNranzahlgesamtDauerdurchschnitt
500112626.0
5002000.0
500329849.0
500413333.0
5005199.0

Angelehnt an: S26 Teil 2, Aufgabe 1 (Liste eindeutiger Objekte, Durchschnitt im Zeitraum) und W24 Teil 2, Aufgabe 1 (Ergebnis-Array je Streckenabschnitt)

ALG-02 · Arrays und Listen durchlaufen

Aufgabe ALG-02.1 ★☆☆ Grundlagen 5 Punkte
2 Pa) Nennen Sie zwei Unterschiede zwischen einem Array und einer Liste (List<T>).
1 Pb) In Java wird für die Ausleihen je Stunde ein Array angelegt: int[] belegung = new int[24]; Geben Sie den kleinsten und den größten gültigen Index an.
1 Pc) Beschreiben Sie, was beim Ausführen der Anweisung belegung[24] = 5; passiert.
1 Pd) Für 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.
Aufgabe ALG-02.2 ★★☆ Anwendung 10 Punkte

Für die Planung des Serviceteams werden die Ausleihen an Station 1 ausgewertet (Anlage 4, Array daten : integer[][]).

4 Pa) Entwickeln Sie eine Funktion summeJeTag(daten : integer[][]) : integer[], die für jeden Wochentag (Zeile) die Summe der Ausleihen liefert. Der Index des Ergebnis-Arrays entspricht dem Wochentag.
4 Pb) Entwickeln Sie eine Funktion 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.
2 Pc) Geben Sie die Rückgabewerte beider Funktionen für die Daten aus Anlage 4 an.

Angelehnt an: S23 Teil 2, Aufgabe 2 und S22 Teil 2, Aufgabe 1 (2D-Arrays Tag × Stunde bzw. Monat × Verbraucher)

Aufgabe ALG-02.3 ★★★ Prüfungsniveau 16 Punkte

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.

5 Pa) Entwickeln Sie eine Funktion 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.
1 Pb) Geben Sie das Ergebnis von genutzteRaeder für die Testdaten aus Anlage 3 an.
7 Pc) Entwickeln Sie eine Funktion 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.
3 Pd) Ein Kollege hat c) in Java so gelöst:
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)

ALG-03 · Minimum/Maximum, lineare Suche

Aufgabe ALG-03.1 ★☆☆ Grundlagen 5 Punkte

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
1 Pa) Geben Sie an, welchen Wert die Funktion für ein Array mit den Rädern 101 bis 105 (Anlage 3) liefert.
2 Pb) Erläutern Sie den Fehler.
1 Pc) Geben Sie die korrigierte Zeile an. Die Schleife soll weiterhin beim Index 1 beginnen.
1 Pd) Nennen Sie eine Bedingung, die das übergebene Array nach Ihrer Korrektur erfüllen muss.

Angelehnt an: W25 Teil 2, Aufgabe 3b (fehlerhafte Minimum-Funktion)

Aufgabe ALG-03.2 ★★☆ Anwendung 10 Punkte
5 Pa) Entwickeln Sie eine 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.
5 Pb) Entwickeln Sie eine Funktion 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

Aufgabe ALG-03.3 ★★★ Prüfungsniveau 20 Punkte

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).

2 Pa) Berechnen Sie die Entfernung zwischen einem Kunden am Standort (4,0 | 3,0) und Station 3. Der Rechenweg ist anzugeben.
12 Pb) Entwickeln Sie eine Funktion 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.
3 Pc) Ermitteln Sie mithilfe der Testdaten, welche Station Ihre Funktion für einen Kunden am Standort (4,0 | 3,0) mit benoetigt = 2 liefert. Begründen Sie, warum es nicht die nächstgelegene Station ist.
3 Pd) Ein Kollege möchte in b) die Variable für die bisher kleinste Entfernung mit 0 initialisieren. Begründen Sie, warum das falsch ist, und nennen Sie eine geeignete Alternative.

Angelehnt an: W23 Teil 2, Aufgabe 2 (Nearest Neighbour) und W25 Teil 2, Aufgabe 1b

ALG-04 · Speicherbedarf berechnen / Zahlensysteme

Aufgabe ALG-04.1 ★☆☆ Grundlagen 6 Punkte
2 Pa) Ergänzen Sie die Tabelle.
EinheitAnzahl Byte
1 KiB
1 kB
1 MiB
1 GB
2 Pb) Wandeln Sie die Dezimalzahl 202 in eine Binärzahl und in eine Hexadezimalzahl um. Der Rechenweg muss erkennbar sein.
1 Pc) Wandeln Sie die Hexadezimalzahl 7E und die Binärzahl 1011 0110 jeweils in eine Dezimalzahl um.
1 Pd) Geben Sie an, wie viele verschiedene Werte sich mit 12 Bit darstellen lassen.

Angelehnt an: W22 Teil 2, Aufgabe 2 (Dezimal → Binär)

Aufgabe ALG-04.2 ★★☆ Anwendung 8 Punkte

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.

4 Pa) Ergänzen Sie für jedes Feld den kleinstmöglichen geeigneten Datentyp und seinen Speicherbedarf in Byte.
FeldWertebereichDatentypByte
ausleihNr1 bis 3 000 000
kundenNr1 bis 200 000
radNr1 bis 5 000
startStation1 bis 60
startZeitZeitstempel in Millisekunden seit 01.01.1970 (zurzeit ca. 1,8 · 10¹²)
dauer0 bis 1 440 (Minuten)
streckeKommazahl, einfache Genauigkeit reicht aus
preisCent0 bis 50 000 (Preis in Cent)
4 Pb) Berechnen Sie den Speicherbedarf eines Datensatzes sowie den Speicherbedarf aller Datensätze eines Jahres (365 Tage) bei durchschnittlich 2 400 Ausleihen pro Tag in MiB. Runden Sie das Ergebnis auf zwei Nachkommastellen.

Angelehnt an: W22 Teil 2, Aufgabe 3c (Datentypen festlegen)

Aufgabe ALG-04.3 ★★★ Prüfungsniveau 12 Punkte

Bei jeder Rückgabe fotografieren zwei Kameras an der Station das Rad, damit Schäden dokumentiert werden (je Kamera ein Foto). Technische Daten:

6 Pa) Berechnen Sie den Speicherbedarf eines Fotos in MiB sowie den Speicherbedarf aller Fotos eines Jahres in TiB. Der Rechenweg ist anzugeben. Runden Sie die Ergebnisse auf zwei Nachkommastellen.
4 Pb) Die Fotos werden im JPEG-Format gespeichert; dadurch sinkt der Speicherbedarf im Mittel auf 1/15 der Rohdaten. Die Fotos sollen zwei Jahre aufbewahrt werden. Berechnen Sie, wie viele Festplatten mit der Herstellerangabe „8 TB“ mindestens benötigt werden (ohne Redundanz).
2 Pc) Im Betriebssystem wird eine dieser Festplatten mit einer Kapazität von etwa „7,27 TB“ angezeigt. Erläutern Sie die Abweichung.

Angelehnt an: W23 Teil 2, Aufgabe 3b (GiB) und S26 Teil 2, Aufgabe 3b (TiB)

ALG-05 · Sortieren

Aufgabe ALG-05.1 ★☆☆ Grundlagen 5 Punkte
3 Pa) Das Array [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).
DurchlaufInhalt des Arrays nach dem DurchlaufVertauschungen
1
2
3
4
2 Pb) Geben Sie die Zeitkomplexität von Bubblesort im schlechtesten Fall an und nennen Sie ein Sortierverfahren mit besserer Zeitkomplexität.
Aufgabe ALG-05.2 ★★☆ Anwendung 10 Punkte

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.

6 Pa) Entwickeln Sie eine Funktion sortiereNachAkku(raeder : Fahrrad[]) : void, die das Array mit Bubblesort aufsteigend nach dem Akkustand sortiert.
2 Pb) Erweitern Sie Ihre Lösung so, dass der Algorithmus abbricht, sobald in einem Durchlauf keine Vertauschung mehr stattgefunden hat. Geben Sie die geänderten bzw. neuen Zeilen an.
2 Pc) Erläutern Sie den Begriff „stabiles Sortierverfahren“ und geben Sie an, ob Ihr Verfahren stabil ist.

Angelehnt an: W22 Teil 2, Aufgabe 1a (Bubblesort für Objekte)

Aufgabe ALG-05.3 ★★★ Prüfungsniveau 18 Punkte

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).

7 Pa) Entwickeln Sie eine Funktion sortiere(ausleihen : Ausleihe[], vergleiche : Function) : void, die das Array mit einem Sortierverfahren Ihrer Wahl unter Verwendung der übergebenen Vergleichsfunktion sortiert.
3 Pb) Entwickeln Sie die Vergleichsfunktion nachDauerAbsteigend(a : Ausleihe, b : Ausleihe) : integer (längste Ausleihe zuerst).
5 Pc) Entwickeln Sie die Vergleichsfunktion nachStationDannPreis(a : Ausleihe, b : Ausleihe) : integer: aufsteigend nach Startstation, bei gleicher Startstation absteigend nach Preis.
3 Pd) Geben Sie für die Testdaten aus Anlage 3 die Reihenfolge der Ausleihnummern an, die sich bei einer Sortierung mit 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

ALG-06 · Zeichenketten verarbeiten

Aufgabe ALG-06.1 ★☆☆ Grundlagen 4 Punkte
2 Pa) Gegeben ist code : string := "AR-0107-8". Geben Sie die Ergebnisse der Ausdrücke an (Methoden siehe Anlage 5).
AusdruckErgebnis
code.length()
code.charAt(3)
code.substring(3, 7)
code.indexOf('-')
2 Pb) Erläutern Sie, warum Zeichenketten in Java mit equals() und nicht mit == verglichen werden sollten.
Aufgabe ALG-06.2 ★★☆ Anwendung 10 Punkte

Bei der Registrierung in der App muss ein Passwort folgende Regeln erfüllen:

8 Pa) Entwickeln Sie eine Funktion pruefePasswort(pw : string) : boolean, die true liefert, wenn alle Regeln erfüllt sind, sonst false. Verwenden Sie die Hilfsfunktionen aus Anlage 5.
2 Pb) Geben Sie für die folgenden Passwörter das Ergebnis Ihrer Funktion an.
PasswortErgebnis
Rad2026!
auenrad#2026
Auenrad#2026
Auen Rad#2026

Angelehnt an: W21 Teil 2, Aufgabe 1c (Passwortprüfung)

Aufgabe ALG-06.3 ★★★ Prüfungsniveau 14 Punkte

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.

6 Pa) Entwickeln Sie eine Funktion 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.
Beispiele: "Schulze-Ebert, Anna Lena" → ["Anna Lena", "Schulze-Ebert"]; "Haas,Tom" → ["Tom", "Haas"].
6 Pb) Entwickeln Sie eine Funktion 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.
Beispiel: "AR-0107-8" ist gültig (0 + 1 + 0 + 7 = 8), "AR-0107-7" und "AR-01O7-8" (Buchstabe O) sind ungültig.
2 Pc) Nennen Sie zwei Probleme, die beim automatischen Zerlegen der Namen aus dem Altsystem auftreten können.

Angelehnt an: W25 Teil 2, Aufgabe 4d (Namen per SUBSTRING aufteilen) und W21 Teil 2, Aufgabe 1c

ALG-07 · Exceptions / Fehlerbehandlung

Aufgabe ALG-07.1 ★☆☆ Grundlagen 5 Punkte
3 Pa) Erläutern Sie den Unterschied zwischen checked und unchecked Exceptions in Java und nennen Sie zu jeder Art ein Beispiel.
2 Pb) Beschreiben Sie, wann der finally-Block ausgeführt wird, und nennen Sie einen typischen Einsatzzweck.
Aufgabe ALG-07.2 ★★☆ Anwendung 9 Punkte

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;
}
6 Pa) Geben Sie für die vier Aufrufe die Bildschirmausgabe der Methode und den Rückgabewert an. Gibt die Methode keinen Wert zurück, beschreiben Sie, was stattdessen geschieht.
AufrufAusgabeRückgabewert
centProMinute("750", 30)
centProMinute("7,50", 30)
centProMinute("750", 0)
centProMinute("750", -5)
3 Pb) Ein Kollege schlägt vor, statt der beiden catch-Blöcke einen einzigen Block catch (Exception e) { } zu verwenden. Beurteilen Sie den Vorschlag.
Aufgabe ALG-07.3 ★★★ Prüfungsniveau 14 Punkte

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:

3 Pa) Nennen Sie drei Fehlerfälle bzw. Sonderfälle, die beim Aufruf von durchschnittsDauer(ausleihen : List<Ausleihe>, stationNr : integer) : double auftreten können.
7 Pb) Entwickeln Sie die Funktion 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.
4 Pc) Entwickeln Sie eine Funktion 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)

ALG-08 · Rekursion

Aufgabe ALG-08.1 ★☆☆ Grundlagen 4 Punkte
2 Pa) Erläutern Sie die Begriffe Basisfall und Rekursionsfall.
2 Pb) Beschreiben Sie, was geschieht, wenn eine rekursive Funktion keinen erreichbaren Basisfall hat.
Aufgabe ALG-08.2 ★★☆ Anwendung 8 Punkte

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
5 Pa) Führen Sie einen Schreibtischtest für den Aufruf binaer(13) durch. Tragen Sie jeden Aufruf (auch die rekursiven) in eine Zeile der Tabelle ein. Nicht benötigte Zeilen bleiben leer.
Aufrufnn div 2n mod 2Rückgabewert
1
2
3
4
5
6
3 Pb) Für einen zulässigen Aufrufwert liefert die Funktion ein falsches Ergebnis. Geben Sie diesen Wert an und korrigieren Sie die Funktion.

Angelehnt an: W22 Teil 2, Aufgabe 2a (Schreibtischtest und Korrektur einer rekursiven Umwandlung)

Aufgabe ALG-08.3 ★★★ Prüfungsniveau 12 Punkte

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.

6 Pa) Entwickeln Sie eine rekursive Funktion 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).
2 Pb) Gegeben ist 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.
4 Pc) Nennen Sie je einen Vorteil und einen Nachteil der rekursiven Lösung gegenüber einer iterativen Lösung und begründen Sie diese kurz.

Angelehnt an: W22 Teil 2, Aufgabe 2b (Rekursion vs. Iteration)

ALG-09 · Nebenläufigkeit

Aufgabe ALG-09.1 ★☆☆ Grundlagen 4 Punkte
2 Pa) Erläutern Sie den Begriff Race Condition.
2 Pb) Nennen Sie zwei Stellen in der Plattform Auenrad 2.0, an denen gleichzeitige Zugriffe zu Fehlern führen können.
Aufgabe ALG-09.2 ★★☆ Anwendung 8 Punkte

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:

ZeitThread A (Jana)Thread B (Tom)freieLastenraeder in der DB
t1liest freieLastenraeder → 11
t2liest freieLastenraeder → 11
t3prüft 1 > 0 → ja1
t4prüft 1 > 0 → ja1
t5schreibt 1 − 1 = 0, speichert Reservierung0
t6schreibt 1 − 1 = 0, speichert Reservierung0
4 Pa) Erläutern Sie anhand des Ablaufs, welches Problem entsteht und wodurch es verursacht wird.
4 Pb) Erläutern Sie, wie pessimistisches Sperren das Problem verhindert, und beschreiben Sie den geänderten Ablauf.

Angelehnt an: W21 Teil 2, Aufgabe 1b (Blockieren von Terminen bei gleichzeitigen Buchungen)

Aufgabe ALG-09.3 ★★★ Prüfungsniveau 14 Punkte

Lastenräder sollen künftig über die App im Voraus reserviert werden können. Das Entwicklungsteam diskutiert zwei Varianten:

6 Pa) Beschreiben Sie für beide Varianten je einen Vorteil und einen Nachteil.
VarianteVorteilNachteil
A
B
3 Pb) Entscheiden Sie sich begründet für eine Variante. Gehen Sie dabei auch auf die Sperrdauer ein.
5 Pc) Entwickeln Sie mithilfe der Klassen aus Anlage 6 eine Funktion 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

Originalaufgaben zum Üben

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üfungAufgabeThemaDatei · Lösung
Winter 2021T2 A2 (25 P)Pseudocode im 2D-Terminplan (Suche)2021/winter/AE 2021 Winter Teil 2.pdf · keine Lösung
Winter 2021T2 A1b (4 P), A1c (6 P)Nebenläufigkeit (Termine blockieren); Passwortprüfung im Struktogramm, Fehler findenwie oben · keine Lösung
Sommer 2022T2 A1 (30 P)Statistik aus 2D-Array, Min/Max, Liste2022/sommer/AE 2022 Sommer Teil 2.pdf · keine Lösung
Sommer 2022T1 A3cb (4 P)notifyObservers in Pseudocode (Liste durchlaufen)2022/sommer/AE 2022 Sommer Teil 1.pdf · keine Lösung
Winter 2022T2 A1 (25 P)Bubblesort, Vergleichsfunktion, generisches Sortieren2022/winter/AE 2022 Winter Teil 2.pdf · keine Lösung
Winter 2022T2 A2 (25 P), A3c (3 P)Rekursion Dezimal → Binär, Schreibtischtest, iterative Variante; Datentypenwie oben · keine Lösung
Sommer 2023T2 A2 (25 P)2D-Array Besucher Tag × Stunde2023/sommer/AE Sommer 2023 Teil 2.pdf · Lösung: AE Sommer 2023 Teil 1+2 Lösungen.pdf
Sommer 2023T1 A2bc, A2bd (9 P)Methoden zu einem Klassendiagramm in Pseudocode2023/sommer/AE Sommer 2023 Teil 1.pdf · Lösung wie oben
Winter 2023T2 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 2023T2 A3b (7 P)Speicherbedarf in GiBwie oben
Sommer 2024T1 A3b (15 P)Methoden mit Listen, lineare Suche2024/sommer/AE Sommer 2024 Teil 1+2+3.pdf · Lösung: AE Sommer 2024 Teil 1 Lösungen.pdf
Sommer 2024T2 A2 (20 P)Sortieren mit Vergleichsfunktion, Zählfunktionwie oben · Lösung: AE Sommer 2024 Teil 2 Lösungen.pdf
Winter 2024T1 A2c (8 P)Skript: Subnetz durchlaufen, JSON-Ausgabe2024/winter/AE Winter 2024 Teil 1.pdf · keine Lösung
Winter 2024T2 A1 (25 P), A2 (25 P)Verspätungen je Strecke zählen (Ergebnis-Array); HTTP-Antwort bauen, nächste Abfahrten filtern2024/winter/AE Winter 2024 Teil 2.pdf · keine Lösung
Winter 2025T2 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 2025T2 A3b (14 P), A4d (8 P)fehlerhafte Minimum-Funktion; Namen per SUBSTRING aufteilenwie oben
Sommer 2026T2 A1 (30 P), A2ba (4 P)Liste eindeutiger Objekte, Durchschnitt im Zeitraum; Liste filtern2026/sommer/AE Sommer 2026 Teil 2.pdf · keine Lösung
Sommer 2026T2 A3b (5 P)Speicherbedarf in TiBwie oben