EuraStudy
Notizen/Informatik/INF-Alg - Algorithmen, Programmierung und Komplexität
AT · Matura

INF-Alg - Algorithmen, Programmierung und Komplexität

Algorithmen sind endliche, eindeutige Handlungsvorschriften. Prüfungsrelevant sind Kontrollstrukturen, Rekursion, klassische Sortier- und Suchverfahren sowie die Beschreibung von Laufzeiten mit Big-O.

6 Abschnitte·~23 Min Lesezeit·4 Kompetenzen·Stand 06/2026

T·0222 / 12
Prüfungsprofil
INF-Alg-1 · Kontrollstrukturen, Funktionen und Modularisierung verstehenINF-Alg-2 · Iterative und rekursive Algorithmen entwerfenINF-Alg-3 · Sortier- und Suchverfahren analysierenINF-Alg-4 · Laufzeit- und Speicherkomplexität mit Big-O beschreiben
Tiefe

Lesetiefe: Vertiefung

Schrift

Schriftgröße: Standard · Zeilenabstand: Kompakt

Medien immer laden: aus

Inhalt · 6 Abschnitte▾
  1. INF-Alg - Algorithmen, Programmierung und Komplexität
    • 01Kontrollstrukturen, Funktionen und Modularisierung○
    • 02Rekursion und Iteration◐
    • 03Sortierverfahren - Bubble, Insertion, Merge, Quicksort◐
    • 04Suchverfahren - linear und binaer○
    • 05Big-O, Master-Theorem und Grenzen●
    • 06Greedy- und Dynamic-Programming-Verfahren●

6 Abschnitte · 30 Merksätze · 7 Formeln · 30 Fehlerwarnungen

§ 01
§ 01

Kontrollstrukturen, Funktionen und Modularisierung#

~4 Min Lesezeit●○○BasisLPINF-Alg-1.1LPINF-Alg-1.2

Kernpunkte

Jeder Algorithmus, so komplex er auch sei, ist aus einem winzigen Wortschatz gebaut. Das Theorem von Böhm/Jacopini (1966) beweist, dass drei Kontrollstrukturen für jede berechenbare Funktion genügen: Sequenz (Anweisungen nacheinander), Selektion (if/else - eine Verzweigung anhand einer Bedingung wählen) und Iteration (while/for - wiederholen, solange eine Bedingung gilt). Ein GOTO ist nie nötig, und jedes Programm lässt sich als verschachtelte Kombination dieser drei lesen.
Eine Funktion bündelt ein Stück Verhalten hinter einem Namen, nimmt Parameter als Eingabe und liefert einen Rückgabewert. Das bringt drei Vorteile: Wiederverwendung (einmal schreiben, oft aufrufen), Abstraktion (der Aufrufer muss die Interna nicht kennen) und Testbarkeit (eine Funktion mit klarer Ein-/Ausgabe lässt sich isoliert prüfen). Der Vertrag lautet: gleiche Eingabe, gleiche Ausgabe.
Modularisierung führt das weiter und zerlegt ein Programm in unabhängige Bausteine mit je einer klaren Aufgabe (). Das senkt die kognitive Last, ermöglicht arbeitsteilige Entwicklung und lokalisiert Änderungen - eine Korrektur in einem Modul bricht selten ein anderes. Eine typische Zerlegung trennt etwa `einlesen()`, `berechnen()` und `ausgeben()` als eigene Funktionen unter einem schlanken Hauptprogramm.

Modularisierung - Aufrufgraph

ModularisierungNetzgraph, Hauptprogramm → einlesen(), Hauptprogramm → berechnen(), Hauptprogramm → ausgeben(), berechnen() → hilfsfunktion()Hauptprogrammeinlesen()berechnen()ausgeben()hilfsfunktion()
Abb. 1Ein kleines Hauptprogramm ruft spezialisierte Funktionen auf; jede Funktion hat genau eine Aufgabe.
Abb. 1 ↓
Variablen haben eine Sichtbarkeit (Scope): Eine lokale Variable lebt nur innerhalb ihrer Funktion und verschwindet beim Rücksprung; eine globale Variable ist überall sichtbar, koppelt Module aber unsichtbar aneinander und ist eine häufige Fehlerquelle. Daten besser über Parameter übergeben, als auf Globale zuzugreifen.
Ein Seiteneffekt ist jede Wirkung über den Rückgabewert hinaus (eine globale Variable ändern, Datei- oder Bildschirm-I/O). Reine Funktionen - ohne Seiteneffekte, Ausgabe hängt nur von der Eingabe ab - sind deutlich leichter zu testen, zu parallelisieren und zu verstehen. Vorhandene Seiteneffekte einer Funktion sollten dokumentiert sein.
Codeausschnitt Python: ```python def bmi(masse_kg, größe_m): return masse_kg / (größe_m ** 2) for person in personen: print(person["name"], bmi(person["masse"], person["größe"])) ```

Vokabeln

→ Kartei
  • KontrollstrukturSprachmittel für Verzweigung und Wiederholung.
  • ParameterBezeichner in der Funktionsdefinition; das Argument ist der übergebene Wert.
  • RückgabewertErgebnis, das die Funktion an die aufrufende Stelle liefert.
  • GültigkeitsbereichBereich, in dem ein Bezeichner sichtbar ist; lokal oder global.
  • ModularisierungZerlegung eines Programms in getrennt testbare Bausteine.
  • SeiteneffektÄnderung eines Zustands außerhalb der Funktion; erschwert das Testen.
Musterbeispiel

Pseudocode -> Python

Übersetze den Pseudocode in Python: "Solange n > 1 wiederhole: wenn n gerade, n := n/2; sonst n := 3n+1. Zähle Schritte."

  1. 01Funktionssignatur

    `def collatz_schritte(n: int) -> int:`

  2. 02Schleife & Verzweigung

    ```python schritte = 0 while n > 1: n = n // 2 if n % 2 == 0 else 3 * n + 1 schritte += 1 return schritte ```

  3. 03Testfall

    `collatz_schritte(6)` ergibt 8 (6,3,10,5,16,8,4,2,1).

Ergebnis: Die Funktion liefert die Länge der Collatz-Folge ab Startwert nnn. Sie terminiert empirisch für alle bisher getesteten Werte - mathematisch unbewiesen!

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Jedes Programm lässt sich aus Sequenz, Selektion und Iteration aufbauen - das ist das Theorem von Böhm/Jacopini.

  2. 2

    Funktionen sind die wichtigste Abstraktionsstufe: gleiche Eingabe ergibt gleiche Ausgabe, ohne Seiteneffekte ist Testen einfach.

  3. 3

    Modularisiere frühzeitig, damit jede Funktion einen einzigen klaren Zweck erfüllt.

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Erkläre am Beispiel einer selbst gewählten Aufgabe die Bedeutung der drei elementaren Kontrollstrukturen Sequenz, Selektion und Iteration. Schreibe die Lösung in Pseudocode oder einer Programmiersprache deiner Wahl.

Maturafokus

  • Übersetze Pseudocode konsequent in EINE Sprache und mische keine Syntaxelemente verschiedener Sprachen.
  • Trenne Eingabe (Parameter), Verarbeitung (Rumpf) und Ausgabe (Rückgabewert) klar voneinander.
  • Skizziere zu jeder Funktion ein Aufrufbeispiel mit konkreten Argumenten und erwartetem Ergebnis.
  • Prüfe Schleifenbedingungen an den Rändern: Läuft die Schleife bei leerer Eingabe null Mal, bei einem Element genau einmal?
  • Begründe die Modularisierung: Kleine Funktionen sind einzeln testbar und mehrfach verwendbar.

Typische Fehler

  • Das `return` steht innerhalb der Schleife und beendet sie nach dem ersten Durchlauf.
  • Eine globale Variable wird in der Funktion überschrieben, statt sie als Parameter zu übergeben.
  • Pseudocode vermischt sprachspezifische Details wie Semikolons und Einrückungsregeln.
  • Die Randfälle bleiben ungetestet; leere Liste oder ein einzelnes Element decken die meisten Fehler auf.
  • Die Funktion gibt nichts zurück und schreibt stattdessen direkt auf die Ausgabe - damit ist sie nicht weiterverwendbar.

§ 01

Aktive Wiederholung

Schreibe eine Python-Funktion `vokale_zählen(text)`, die die Anzahl der Vokale (a, e, i, o, u; groß/klein) zurückliefert. Demonstriere den Aufruf mit "Informatik".

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: Böhm/Jacopini: Flow diagrams, Turing machines and languages with only two formation rules (CACM) · Python Software Foundation - Tutorial (PSF)

§ 02
§ 02

Rekursion und Iteration#

~4 Min Lesezeit●●○StandardLPINF-Alg-2.1

Kernpunkte

Rekursion löst ein Problem, indem es auf eine kleinere Instanz desselben Problems zurückgeführt wird: Eine rekursive Funktion ruft sich selbst mit „kleineren" Argumenten auf. Das ist oft die natürlichste Beschreibung selbstähnlicher Strukturen - Bäume, Listen, Divide-and-Conquer - und entspricht mathematisch einer Rekurrenz wie n!=n⋅(n−1)!n! = n\cdot(n-1)!n!=n⋅(n−1)!.
Jede Rekursion braucht zwei Teile: einen Basisfall, der ohne weiteren Selbstaufruf terminiert (0!=10! = 10!=1), und einen Rekursionsfall, der das Problem verkleinert und sich selbst aufruft (n!=n⋅(n−1)!n! = n\cdot(n-1)!n!=n⋅(n−1)!). Fehlt der Basisfall - oder verkleinert der Rekursionsfall das Argument nicht -, läuft die Funktion endlos, bis der Speicher voll ist.
Der Mechanismus dahinter ist der Aufruf-Stack (Call-Stack): Bei jedem Aufruf legt das Laufzeitsystem einen Stack-Frame mit Parametern, lokalen Variablen und Rückkehradresse ab; ist der Basisfall erreicht, werden die Frames in umgekehrter Reihenfolge abgebaut. Die maximale Rekursionstiefe bestimmt damit den Speicherbedarf - zu tiefe Rekursion (etwa n=10000n=10000n=10000) löst in Python einen `RecursionError` bzw. einen Stack Overflow aus.
Wie teuer Rekursion werden kann, zeigt der Rekursionsbaum. Beim naiven Fibonacci F(n)=F(n−1)+F(n−2)F(n)=F(n-1)+F(n-2)F(n)=F(n−1)+F(n−2) verzweigt jeder Aufruf in zwei, der Baum hat ≈φn\approx \varphi^{n}≈φn Knoten, und Teilprobleme werden mehrfach berechnet ( zeigt, wie fib(2)fib(2)fib(2) doppelt auftaucht). Daher ist die naive Laufzeit exponentiell, nicht linear.

Rekursionsbaum von fib(4)

Rekursionsbaum fib(4)Baumdiagramm, 4 Pfade, Daten: fib(3) → fib(2) → fib(1); fib(3) → fib(2) → fib(0); fib(3) → fib(1); fib(2)fib(2)fib(3)fib(4)fib(1)fib(0)fib(1)fib(2)
Abb. 2Jeder Aufruf verzweigt in zwei; fib(2) wird zweimal berechnet (hervorgehoben) - die Redundanz motiviert Memoization.
Abb. 2 ↓
Das Heilmittel ist Memoization: einmal berechnete Werte in einer Tabelle merken, sodass jeder Teilwert nur einmal entsteht - das senkt Fibonacci von O(φn)O(\varphi^{n})O(φn) auf O(n)O(n)O(n). Allgemein sind iterative Lösungen meist speicherärmer (O(1)O(1)O(1) statt O(n)O(n)O(n) Stack), rekursive oft lesbarer. Endrekursion (Tail Recursion), bei der der Selbstaufruf die letzte Aktion ist, können viele Compiler in eine Schleife umwandeln - Python tut das allerdings nicht.
Klassische rekursive Verfahren sind Fakultät, Fibonacci, Türme von Hanoi, Quicksort, Mergesort und Baumtraversierungen. Typische Fehler: den Basisfall vergessen (Endlosrekursion), das `return` in der rekursiven Funktion weglassen, oder naives Fibonacci für effizient halten. In der Prüfung immer Basis- und Rekursionsfall explizit benennen und Tiefe bzw. Aufrufzahl an einem kleinen Beispiel begründen.

Vokabeln

→ Kartei
  • BasisfallFall ohne weiteren rekursiven Aufruf; er beendet die Rekursion.
  • RekursionstiefeGrößte Zahl gleichzeitig offener Aufrufe.
  • AufrufstapelSpeicherbereich für die offenen Funktionsaufrufe.
  • MemoizationZwischenspeichern bereits berechneter Teilergebnisse.
  • EndrekursionRekursiver Aufruf als letzte Anweisung; in manchen Sprachen in eine Schleife übersetzbar.
  • TerminierungGarantie, dass das Verfahren nach endlich vielen Schritten endet.

Rekursive Definition der Fakultät

n!={1,n=0n⋅(n−1)!,n>0n! = \begin{cases} 1, & n = 0 \\ n \cdot (n-1)!, & n > 0 \end{cases}n!={1,n⋅(n−1)!,​n=0n>0​

Die obere Zeile ist der Basisfall, die untere der Rekursionsfall. Ohne den Basisfall terminiert die Rekursion nicht; die Festlegung 0!=10!=10!=1 hält die Formeln in den Randfällen gültig.

Fibonacci-Rekurrenz

F(n)=F(n−1)+F(n−2), F(0)=0, F(1)=1F(n) = F(n-1) + F(n-2),\ F(0) = 0,\ F(1) = 1F(n)=F(n−1)+F(n−2), F(0)=0, F(1)=1

Die naive Umsetzung dieser Rekurrenz berechnet dieselben Teilwerte immer wieder und läuft exponentiell. Mit Zwischenspeicherung oder iterativ sinkt der Aufwand auf O(n)O(n)O(n) - das ist das Standardbeispiel für den Nutzen der Memoization.

Musterbeispiel

Fibonacci - rekursiv vs. memoization

Implementiere `fib(n)` rekursiv und mit Memoization in Python.

  1. 01Naive Rekursion

    ```python def fib(n): if n < 2: return n return fib(n-1) + fib(n-2) ``` Laufzeit T(n)=T(n−1)+T(n−2)+1∈O(φn)T(n) = T(n-1)+T(n-2)+1 \in O(\varphi^{n})T(n)=T(n−1)+T(n−2)+1∈O(φn) mit φ=1+52\varphi=\tfrac{1+\sqrt 5}{2}φ=21+5​​.

  2. 02Memoization

    ```python from functools import lru_cache @lru_cache def fib(n): return n if n < 2 else fib(n-1)+fib(n-2) ``` Laufzeit O(n)O(n)O(n), Speicher O(n)O(n)O(n).

  3. 03Iterativ

    ```python def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a+b return a ``` Laufzeit O(n)O(n)O(n), Speicher O(1)O(1)O(1).

Ergebnis: Rekursion ist elegant, aber ohne Caching zu langsam; Memoization oder Iteration sind die richtigen Lösungen für große nnn.

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Rekursion zerlegt ein Problem in eine kleinere Version desselben Problems plus einen Basisfall.

  2. 2

    Der Call-Stack ist die Daten-struktur, die Rekursion überhaupt erst möglich macht.

  3. 3

    Memoization speichert berechnete Werte und verwandelt exponentielle Rekursion in lineare Laufzeit.

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Beschreibe das Prinzip der Rekursion und veranschauliche es am Beispiel der Fakultätsfunktion. Implementiere die Funktion in einer gewählten Programmiersprache und erläutere ihre Laufzeit sowie Speicherbedarf.

Maturafokus

  • Benenne bei jeder rekursiven Funktion Basisfall UND Rekursionsfall ausdrücklich.
  • Begründe die Terminierung: Jeder Aufruf muss dem Basisfall näher kommen.
  • Bestimme Anzahl der Aufrufe und maximale Rekursionstiefe an einem kleinen Beispiel.
  • Nenne beim naiven Fibonacci die exponentielle Laufzeit und Memoization als Abhilfe.
  • Vergleiche rekursive und iterative Lösung nach Lesbarkeit, Speicherbedarf und Laufzeit.

Typische Fehler

  • Der Basisfall fehlt; die Rekursion terminiert nicht und der Aufrufstapel läuft über.
  • Das Ergebnis des rekursiven Aufrufs wird nicht zurückgegeben.
  • Naives Fibonacci wird als effizient bezeichnet; es rechnet dieselben Teilprobleme vielfach.
  • Rekursionstiefe und Anzahl der Aufrufe werden verwechselt.
  • Endrekursion wird als automatisch optimiert angenommen; nicht jede Sprache tut das.

§ 02

Aktive Wiederholung

Schreibe eine rekursive Python-Funktion `fakultät(n)`. Erläutere, warum sie bei n=10000n=10000n=10000 in Python einen `RecursionError` auslösen kann.

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: Cormen, Leiserson, Rivest, Stein: Introduction to Algorithms (CLRS) Kap. 2-4 (MIT Press)

§ 03
§ 03

Sortierverfahren - Bubble, Insertion, Merge, Quicksort#

~4 Min Lesezeit●●○StandardLPINF-Alg-3.1

Logarithmisches Wachstum O(log n) bei wachsendem n

Logarithmisches Wachstum O(log n)Schaubild, Nullstellen bei x = 1, steigend, im Bereich x von 1 bis 10242004006008001000246810n = 646 SchritteSchritte (relativ)n
Abb. 5Eine Verdopplung von n erhöht die Schrittzahl nur um eine Konstante - typisch für die binäre Suche. Markiert: n = 64 mit etwa 6 Schritten.

Kernpunkte

Sortieren ist das Standardproblem der Informatik - an ihm lassen sich alle Kernkonzepte zeigen: Verfahrensidee, Laufzeit, Speicher und Stabilität. Ein zentrales Ergebnis vorweg: Jedes vergleichsbasierte Verfahren braucht mindestens Ω(nlog⁡n)\Omega(n\log n)Ω(nlogn) Vergleiche. Das folgt aus einem Entscheidungsbaum-Argument - es gibt n!n!n! mögliche Anordnungen, jeder Vergleich halbiert die Restmenge, und log⁡2(n!)≈nlog⁡n\log_{2}(n!) \approx n\log nlog2​(n!)≈nlogn.
Bubble Sort vergleicht benachbarte Elemente und tauscht sie, bis nichts mehr zu tauschen ist - sehr einfach, aber im Mittel und Worst Case O(n2)O(n^{2})O(n2). Insertion Sort fügt jedes neue Element an die richtige Stelle des bereits sortierten Präfix ein; ebenfalls O(n2)O(n^{2})O(n2), aber nur O(n)O(n)O(n) im Best Case (fast sortierte Daten) und mit geringem Overhead - daher ideal für kleine nnn.
Mergesort ist Divide-and-Conquer: Das Array wird halbiert, beide Hälften rekursiv sortiert und dann in Linearzeit verschmolzen (). Die Rekurrenz T(n)=2T(n/2)+nT(n)=2T(n/2)+nT(n)=2T(n/2)+n löst sich zu O(nlog⁡n)O(n\log n)O(nlogn), und zwar in allen Fällen. Der Preis ist O(n)O(n)O(n) Zusatzspeicher fürs Mischen; dafür ist Mergesort stabil.

Mergesort - Divide-and-Conquer Baum

Mergesort-BaumBaumdiagramm, 7 Pfade, Daten: [38,27,43,3] → [38,27] → [38]; [38,27,43,3] → [38,27] → [27]; [38,27,43,3] → [43,3] → [43]; [38,27,43,3] → [43,3] → [3]; [9,82,10] → [9,82] → [9]; [9,82,10] → [9,82] → [82]; [9,82,10] → [10][38,27][43,3][38,27,43,3][9,82][9,82,10][38,27,43,3,9,82,10][38][27][43][3][9][82][10]
Abb. 3Divide: das Array wird rekursiv halbiert bis zu Einzelelementen. Conquer (bottom-up): [27,38]+[3,43] ergibt [3,27,38,43], [9,82]+[10] ergibt [9,10,82], am Ende [3,9,10,27,38,43,82].
Abb. 3 ↓
Quicksort wählt ein Pivot und partitioniert das Array in „<<< Pivot" und „≥\geq≥ Pivot" (), dann rekursiv weiter. Im Mittel O(nlog⁡n)O(n\log n)O(nlogn) und in-place (wenig Speicher), im Worst Case aber O(n2)O(n^{2})O(n2) - etwa bei bereits sortiertem Array mit dem ersten Element als Pivot; randomisierte Pivots oder Median-of-three entschärfen das. Quicksort ist nicht stabil.

Quicksort - Partition mit Pivot

Quicksort-Partition (Pivot 5)Tabelle mit 9 Spalten und 2 Zeilen, Daten: a0 · a1 · a2 · a3 · a4 · a5 · a6 · a7; vor · 8 · 3 · 1 · 7 · 5 · 2 · 6 · 4; nach · 3 · 1 · 2 · 4 · 5 · 8 · 7 · 6, hervorgehobene Zelle: 5a0a1a2a3a4a5a6a7vor83175264nach31245876
Abb. 4Das Pivot 5 trennt das Array in „kleiner Pivot" (links) und „größer/gleich Pivot" (rechts) und steht danach an seiner endgültigen Position (Index a4). Mittel O(n log n), Worst Case O(n^2).
Abb. 4 ↓
Stabilität heißt: Elemente mit gleichem Schlüssel behalten ihre relative Reihenfolge. Das ist beim mehrstufigen Sortieren wichtig (erst nach Vorname, dann stabil nach Nachname - sonst zerfällt die erste Ordnung). Stabil sind Mergesort und Insertion Sort, nicht stabil Quicksort und Heapsort.
In der Praxis nutzen Standardbibliotheken Timsort (Python `sorted`, Java `Arrays.sort` für Objekte), eine Hybride aus Mergesort und Insertion Sort, die natürliche bereits sortierte Läufe ausnutzt. Häufige Fehler: den Quicksort-Worst-Case mit O(nlog⁡n)O(n\log n)O(nlogn) angeben (richtig ist O(n2)O(n^{2})O(n2)), Mergesort als in-place bezeichnen (es braucht O(n)O(n)O(n)), oder Quicksort für stabil halten.

Vokabeln

→ Kartei
  • StabilitätGleiche Schlüssel behalten ihre ursprüngliche Reihenfolge.
  • in-placeVerfahren mit nur konstantem zusätzlichem Speicherbedarf.
  • PivotTrennelement bei Quicksort; seine Wahl entscheidet über den Worst Case.
  • Divide and ConquerZerlegen, rekursiv lösen, zusammenfügen; Prinzip von Mergesort und Quicksort.
  • VergleichssortierungVerfahren, das nur Elementvergleiche nutzt; untere Schranke $\Omega(n\log n)$.
  • Worst CaseUngünstigste Eingabe; sie bestimmt die Garantie eines Verfahrens.

Mergesort-Rekurrenz und Lösung

T(n)=2T ⁣(n2)+n∈O(nlog⁡n)T(n) = 2T\!\left(\tfrac{n}{2}\right) + n \in O(n\log n)T(n)=2T(2n​)+n∈O(nlogn)

Der Faktor 2 steht für die beiden Hälften, nnn für das Verschmelzen. Die Lösung folgt aus dem Master-Theorem (zweiter Fall) und gilt für Best, Average und Worst Case gleichermaßen - das unterscheidet Mergesort von Quicksort.

Wiedergabe lädt Inhalte von YouTube (Google).Auf YouTube öffnen ↗
Wiedergabe lädt Inhalte von YouTube (Google).Auf YouTube öffnen ↗
Musterbeispiel

Mergesort-Trace auf [38, 27, 43, 3, 9, 82, 10]

Sortiere [38, 27, 43, 3, 9, 82, 10] mit Mergesort und protokolliere die Merge-Schritte.

  1. Teilen 1

    Links [38,27,43,3] | Rechts [9,82,10].

  2. Teilen 2 (links)

    [38,27] und [43,3] werden zu je [27,38] und [3,43] gemergt.

  3. 03Merge linker Halbteile

    merge([27,38],[3,43]) = [3,27,38,43].

  4. 04Teilen 2 (rechts)

    [9,82] und [10]; merge ergibt [9,10,82].

  5. 05Finaler Merge

    merge([3,27,38,43],[9,10,82]) = [3,9,10,27,38,43,82].

Ergebnis: Sortiertes Array [3, 9, 10, 27, 38, 43, 82]. Laufzeit T(n)=2T(n/2)+n∈O(nlog⁡n)T(n) = 2T(n/2)+n \in O(n \log n)T(n)=2T(n/2)+n∈O(nlogn).

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Sortieren ist das Standardbeispiel, an dem alle Konzepte zusammenkommen: Verfahren, Komplexität, Speicher und Stabilität.

    Mergesort - Divide-and-Conquer Baum

    Mergesort-BaumBaumdiagramm, 7 Pfade, Daten: [38,27,43,3] → [38,27] → [38]; [38,27,43,3] → [38,27] → [27]; [38,27,43,3] → [43,3] → [43]; [38,27,43,3] → [43,3] → [3]; [9,82,10] → [9,82] → [9]; [9,82,10] → [9,82] → [82]; [9,82,10] → [10][38,27][43,3][38,27,43,3][9,82][9,82,10][38,27,43,3,9,82,10][38][27][43][3][9][82][10]
    Abb.Divide: das Array wird rekursiv halbiert bis zu Einzelelementen. Conquer (bottom-up): [27,38]+[3,43] ergibt [3,27,38,43], [9,82]+[10] ergibt [9,10,82], am Ende [3,9,10,27,38,43,82].
  2. 2

    Mergesort teilt das Array bis auf Einzelelemente und fädelt sie sortiert zusammen - daher die garantiert logarithmische Tiefe.

  3. 3

    Quicksort ist schnell, weil die Partition in-place arbeitet, aber er kann bei schlechter Pivotwahl quadratisch werden.

    Quicksort - Partition mit Pivot

    Quicksort-Partition (Pivot 5)Tabelle mit 9 Spalten und 2 Zeilen, Daten: a0 · a1 · a2 · a3 · a4 · a5 · a6 · a7; vor · 8 · 3 · 1 · 7 · 5 · 2 · 6 · 4; nach · 3 · 1 · 2 · 4 · 5 · 8 · 7 · 6, hervorgehobene Zelle: 5a0a1a2a3a4a5a6a7vor83175264nach31245876
    Abb.Das Pivot 5 trennt das Array in „kleiner Pivot" (links) und „größer/gleich Pivot" (rechts) und steht danach an seiner endgültigen Position (Index a4). Mittel O(n log n), Worst Case O(n^2).

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Vergleiche Insertion Sort und Mergesort hinsichtlich Idee, Laufzeit und Speicher. Wann würdest du Insertion Sort, wann Mergesort einsetzen?

Maturafokus

  • Schreibe den Trace eines Verfahrens auf sechs bis acht Elementen Schritt für Schritt nieder.
  • Trenne Best-, Average- und Worst-Case und begründe jeden mit einer konkreten Eingabe.
  • Nenne die untere Schranke Ω(nlog⁡n)\Omega(n\log n)Ω(nlogn) für Vergleichssortierverfahren und das Entscheidungsbaum-Argument dahinter.
  • Beurteile Stabilität und Speicherbedarf zusätzlich zur Laufzeit - beides ist praxisentscheidend.
  • Ordne die Verfahren zu: Insertion Sort bei kleinen oder fast sortierten Daten, Mergesort bei Stabilitätsanforderung, Quicksort als schneller Allrounder.

Typische Fehler

  • Der Worst Case von Quicksort wird mit O(nlog⁡n)O(n\log n)O(nlogn) angegeben; er ist O(n2)O(n^{2})O(n2).
  • Mergesort wird als in-place beschrieben; er braucht zusätzlich O(n)O(n)O(n) Speicher.
  • Quicksort wird als stabil bezeichnet; in der üblichen Implementierung ist er es nicht.
  • Best und Worst Case werden vertauscht: Insertion Sort ist bei bereits sortierten Daten O(n)O(n)O(n), nicht O(n2)O(n^{2})O(n2).
  • Aus einer besseren Big-O-Klasse wird auf schnellere Ausführung bei kleinen nnn geschlossen; dort schlagen die Konstanten durch.

§ 03

Aktive Wiederholung

Sortiere [5, 2, 9, 1, 5, 6] mit Insertion Sort und protokolliere die ersten drei Iterationen. Wie oft wird vergleichen und wie oft getauscht?

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: Knuth: The Art of Computer Programming, Vol. 3 - Sorting and Searching (Addison-Wesley) · OpenDSA - Sorting Algorithms (Virginia Tech)

§ 04
§ 04

Suchverfahren - linear und binaer#

~4 Min Lesezeit●○○BasisLPINF-Alg-3.2

Kernpunkte

Suchen heißt: in einer Datenmenge ein Element mit gegebenem Schlüssel finden. Welche Methode richtig ist, hängt entscheidend davon ab, ob die Daten geordnet sind - das ist der rote Faden dieses Abschnitts ( vergleicht das Wachstum beider Verfahren).

Lineare vs. binäre Suche - Vergleichsschritte

Linear vs. binärSchaubild von O(n), steigend, im Bereich x von 1 bis 64, Schaubild von O(log n), Nullstellen bei x = 1, steigend, im Bereich x von 1 bis 64102030405060102030405060706O(n)O(log n)Schritten
Abb. 6Die lineare Suche wächst mit nnn, die binäre nur mit log⁡2n\log_{2} nlog2​n: bei n=64n=64n=64 genügen 6 statt 64 Vergleiche.
Abb. 6 ↓
Die lineare Suche prüft der Reihe nach jedes Element; sie stellt keine Voraussetzung an die Reihenfolge und braucht im Worst Case nnn Vergleiche, also O(n)O(n)O(n). Für unsortierte oder als Stream eintreffende Daten bleibt sie der Standard.
Die binäre Suche setzt ein sortiertes Array voraus und halbiert in jedem Schritt das Suchintervall: Man vergleicht mit dem mittleren Element und verwirft die Hälfte, in der der Wert nicht liegen kann. So sinkt das Restproblem n→n/2→n/4→⋯→1n \to n/2 \to n/4 \to \dots \to 1n→n/2→n/4→⋯→1; die Rekurrenz T(n)=T(n/2)+1T(n)=T(n/2)+1T(n)=T(n/2)+1 ergibt O(log⁡n)O(\log n)O(logn). Konkret genügen bei n=1 000 000n=1\,000\,000n=1000000 nur ⌈log⁡2(n+1)⌉=20\lceil \log_{2}(n+1) \rceil = 20⌈log2​(n+1)⌉=20 Vergleiche statt bis zu einer Million.
Die Tücke liegt im Detail: mid=(low+high)/2mid=(low+high)/2mid=(low+high)/2 (besser low+(high−low)/2low+(high-low)/2low+(high−low)/2 gegen Überlauf), und die Grenzen müssen je nach Treffer korrekt verschoben werden (low=mid+1low=mid+1low=mid+1 bzw. high=mid−1high=mid-1high=mid−1), sonst entsteht eine Endlosschleife oder ein Off-by-one. Auf einem unsortierten Array liefert die binäre Suche schlicht falsche Ergebnisse.
Für gleichverteilte, sortierte Daten schätzt die Interpolationssuche die Position (wie beim Aufschlagen eines Telefonbuchs) und erreicht im Mittel O(log⁡log⁡n)O(\log \log n)O(loglogn). Hashtabellen suchen sogar in erwartetem O(1)O(1)O(1) - asymptotisch besser als binäre Suche -, kosten aber zusätzlichen Speicher und brauchen eine gute Hashfunktion.
Faustregel: lineare Suche für kleine, unsortierte oder streamende Daten; binäre Suche für große, sortierte, statische Bestände; Hashtabelle für sehr häufige Zugriffe bei vorhandenem Speicher. Häufige Fehler: binäre Suche auf unsortierte Daten anwenden, oder „logarithmisch ist langsamer als linear" - tatsächlich ist O(log⁡n)O(\log n)O(logn) dramatisch schneller als O(n)O(n)O(n).

Vokabeln

→ Kartei
  • lineare SucheSequentielles Durchmustern; $O(n)$, ohne Sortierung möglich.
  • binäre SucheHalbierungssuche in sortierten Daten; $O(\log n)$.
  • SuchraumNoch nicht ausgeschlossener Bereich der Datenmenge.
  • InvarianteAussage, die vor und nach jedem Schleifendurchlauf gilt; Grundlage des Korrektheitsbeweises.
  • Off-by-oneFehler um eins bei Indexgrenzen; häufigste Fehlerquelle der binären Suche.
  • amortisierte BetrachtungKostenverteilung über viele Operationen statt Einzelbetrachtung.

Rekurrenz der binären Suche

Tbin(n)=T(n/2)+1∈O(log⁡n)T_{\text{bin}}(n) = T(n/2) + 1 \in O(\log n)Tbin​(n)=T(n/2)+1∈O(logn)

Jeder Schritt halbiert den Suchraum und kostet einen Vergleich. Daraus folgt die logarithmische Laufzeit: In einer Million Elementen genügen rund 20 Schritte.

Musterbeispiel

Binäre Suche im sortierten Array

Finde mit binärer Suche den Wert 23 im Array [3, 7, 12, 18, 23, 31, 47, 55].

  1. 01Initial

    low = 0, high = 7. Pivot mid = (0+7)/2 = 3 -> a[3] = 18 < 23 -> rechts weitersuchen.

  2. Iteration 2

    low = 4, high = 7. mid = 5 -> a[5] = 31 > 23 -> links weitersuchen.

  3. Iteration 3

    low = 4, high = 4. mid = 4 -> a[4] = 23 == Suchwert. Index 4 zurückgeben.

Ergebnis: Index 4 nach 3 Vergleichen; allgemein O(log⁡n)O(\log n)O(logn) Schritte für nnn Elemente.

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Die binaere Suche ist das Schulbuchbeispiel für logarithmische Laufzeit.

  2. 2

    Bei jeder Halbierung sinkt das Restproblem auf n/2n/2n/2, n/4n/4n/4, ... bis 111.

  3. 3

    Auf unsortierten Daten musst du linear suchen oder zuerst sortieren - das kostet O(nlog⁡n)O(n\log n)O(nlogn).

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Erkläre die binaere Suche im Vergleich zur linearen Suche. Implementiere sie iterativ und nenne Voraussetzungen und Laufzeit.

Maturafokus

  • Protokolliere den Trace der binären Suche mit lowlowlow, highhighhigh und midmidmid in jeder Iteration.
  • Nenne die Voraussetzung ausdrücklich: Die binäre Suche setzt eine SORTIERTE Folge voraus.
  • Vergleiche numerisch: In einer Million Elementen braucht die lineare Suche bis zu 10610^{6}106, die binäre höchstens 20 Schritte.
  • Achte auf die Abbruchbedingung; low≤highlow\le highlow≤high und low<highlow<highlow<high verhalten sich unterschiedlich.
  • Beurteile die Gesamtkosten: Einmal sortieren und dann oft binär suchen lohnt, einmal suchen dagegen nicht.

Typische Fehler

  • Die binäre Suche wird auf ein unsortiertes Feld angewandt.
  • Off-by-one-Fehler bei der Berechnung von midmidmid oder bei der Anpassung der Grenzen führen zu Endlosschleifen.
  • Logarithmisches Wachstum wird als langsamer als lineares beschrieben.
  • Der Aufwand des vorherigen Sortierens bleibt in der Gesamtbetrachtung unberücksichtigt.
  • Bei mehrfach vorkommendem Suchwert wird angenommen, die binäre Suche liefere den ersten Treffer.

§ 04

Aktive Wiederholung

Schreibe eine iterative binaere Suche in JavaScript. Wie viele Iterationen sind im Worst Case bei einer Million Elementen nötig?

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: Sedgewick & Wayne: Algorithms, 4. Auflage (Addison-Wesley)

§ 05
§ 05

Big-O, Master-Theorem und Grenzen#

~4 Min Lesezeit●●●VertiefungLPINF-Alg-4.1LPINF-Alg-4.2

Mergesort - Divide-and-Conquer Baum

Mergesort-BaumBaumdiagramm, 7 Pfade, Daten: [38,27,43,3] → [38,27] → [38]; [38,27,43,3] → [38,27] → [27]; [38,27,43,3] → [43,3] → [43]; [38,27,43,3] → [43,3] → [3]; [9,82,10] → [9,82] → [9]; [9,82,10] → [9,82] → [82]; [9,82,10] → [10][38,27][43,3][38,27,43,3][9,82][9,82,10][38,27,43,3,9,82,10][38][27][43][3][9][82][10]
Abb. 8Divide: das Array wird rekursiv halbiert bis zu Einzelelementen. Conquer (bottom-up): [27,38]+[3,43] ergibt [3,27,38,43], [9,82]+[10] ergibt [9,10,82], am Ende [3,9,10,27,38,43,82].

Kernpunkte

Will man Algorithmen vergleichen, ohne Hardware und Programmiersprache zu vermischen, zählt man nicht Sekunden, sondern wie die Schrittzahl mit der Eingabegröße nnn wächst. Die Big-O-Notation macht das präzise und ist damit die gemeinsame Sprache der Laufzeitanalyse ( zeigt die wichtigsten Klassen im Vergleich).

Wachstum der Komplexitätsklassen

KomplexitätsklassenSchaubild von log n, Nullstellen bei x = 1, steigend, im Bereich x von 1 bis 7, Schaubild von n, steigend, im Bereich x von 1 bis 7, Schaubild von n^2, steigend, im Bereich x von 1 bis 7, Schaubild von 2^n, steigend, im Bereich x von 1 bis 7123456720406080100120140log nnn22nOperationenn
Abb. 7Logarithmisch, linear, quadratisch und exponentiell im Vergleich - das exponentielle 2n2^{n}2n enteilt allen anderen.
Abb. 7 ↓
Formal ist f∈O(g)f \in O(g)f∈O(g), wenn es Konstanten c>0c>0c>0 und n0n_{0}n0​ gibt mit f(n)≤c g(n)f(n) \leq c\,g(n)f(n)≤cg(n) für alle n≥n0n \geq n_{0}n≥n0​. Big-O ist also eine obere asymptotische Schranke: Konstante Faktoren und Terme niedrigerer Ordnung fallen weg, O(3n+5)=O(n)O(3n+5)=O(n)O(3n+5)=O(n). Daneben stehen Ω(g)\Omega(g)Ω(g) (untere Schranke), Θ(g)\Theta(g)Θ(g) (gleiches Wachstum, obere und untere zugleich) und o(g)o(g)o(g) (strikt langsamer wachsend).
Die geläufige Hierarchie lautet O(1)⊂O(log⁡n)⊂O(n)⊂O(nlog⁡n)⊂O(n2)⊂O(2n)⊂O(n!)O(1) \subset O(\log n) \subset O(n) \subset O(n\log n) \subset O(n^{2}) \subset O(2^{n}) \subset O(n!)O(1)⊂O(logn)⊂O(n)⊂O(nlogn)⊂O(n2)⊂O(2n)⊂O(n!). Anschaulich (): konstante und logarithmische Verfahren bleiben selbst bei Milliarden Elementen schnell, quadratische schaffen höchstens Hunderttausende, exponentielle nur Dutzende - das 2n2^{n}2n enteilt allen anderen.
Für Divide-and-Conquer-Rekurrenzen T(n)=a T(n/b)+f(n)T(n) = a\,T(n/b) + f(n)T(n)=aT(n/b)+f(n) liefert das Master-Theorem die Lösung über den Vergleich von f(n)f(n)f(n) mit nlog⁡ban^{\log_{b}a}nlogb​a: Dominiert nlog⁡ban^{\log_{b}a}nlogb​a (Fall 1), ist T∈Θ(nlog⁡ba)T \in \Theta(n^{\log_{b}a})T∈Θ(nlogb​a); sind beide gleich groß (Fall 2), Θ(nlog⁡balog⁡n)\Theta(n^{\log_{b}a}\log n)Θ(nlogb​alogn); dominiert fff (Fall 3), Θ(f(n))\Theta(f(n))Θ(f(n)). Für Mergesort (a=2, b=2, f=na=2,\,b=2,\,f=na=2,b=2,f=n) ist nlog⁡22=n=fn^{\log_{2}2}=n=fnlog2​2=n=f, also Fall 2 und Θ(nlog⁡n)\Theta(n\log n)Θ(nlogn) (siehe Beispiel).
Komplexität fragt „wie teuer?", die Berechenbarkeit fragt „überhaupt lösbar?". Nicht jedes Problem ist algorithmisch entscheidbar - das Halteproblem ist das berühmte Gegenbeispiel. Big-O sagt nichts über die Lösbarkeit, nur über den Aufwand lösbarer Probleme.
Häufige Fehler: Konstanten in Big-O behalten; das Master-Theorem auf Rekurrenzen ohne Divide-and-Conquer-Form anwenden; oder exponentielle Verfahren für „in der Praxis lösbar" halten. Und stets bedenken: Big-O gilt asymptotisch - für kleine nnn kann ein O(n2)O(n^{2})O(n2)-Verfahren ein O(nlog⁡n)O(n\log n)O(nlogn)-Verfahren wegen kleinerer Konstanten schlagen.

Vokabeln

→ Kartei
  • Big-OObere Schranke des Wachstums einer Funktion.
  • OmegaUntere Schranke des Wachstums.
  • ThetaScharfe Schranke; obere und untere Schranke fallen zusammen.
  • Master-TheoremLösungsschema für Rekurrenzen der Form $T(n)=a\,T(n/b)+f(n)$.
  • asymptotischFür sehr große Eingaben betrachtet; Konstanten spielen dort keine Rolle.
  • RekurrenzgleichungGleichung, die die Laufzeit über kleinere Teilprobleme ausdrückt.

Big-O Notation - obere asymptotische Schranke

f(n)∈O(g(n))  ⟺  ∃ c>0, n0≥0:∀ n≥n0:f(n)≤c⋅g(n)f(n) \in O(g(n)) \iff \exists\, c>0,\, n_{0}\geq 0 : \forall\, n \geq n_{0} : f(n) \leq c\cdot g(n)f(n)∈O(g(n))⟺∃c>0,n0​≥0:∀n≥n0​:f(n)≤c⋅g(n)

ccc ist eine positive Konstante, n0n_{0}n0​ die Stelle, ab der die Abschätzung gilt. Die Definition macht deutlich, warum konstante Faktoren wegfallen und die Aussage erst für große nnn gilt.

Master-Theorem - Rekurrenzform für Divide-and-Conquer

T(n)=a⋅T ⁣(nb)+f(n),a≥1, b>1T(n) = a\cdot T\!\left(\tfrac{n}{b}\right) + f(n),\quad a\geq 1,\, b>1T(n)=a⋅T(bn​)+f(n),a≥1,b>1

aaa ist die Zahl der Teilprobleme, bbb der Verkleinerungsfaktor und f(n)f(n)f(n) der Aufwand für Zerlegen und Zusammenfügen. Verglichen wird f(n)f(n)f(n) mit nlog⁡ban^{\log_{b}a}nlogb​a; welcher der beiden dominiert, entscheidet den Fall.

Logarithmisches Wachstum O(log n) bei wachsendem n

Logarithmisches Wachstum O(log n)Schaubild, Nullstellen bei x = 1, steigend, im Bereich x von 1 bis 10242004006008001000246810n = 646 SchritteSchritte (relativ)n
Abb. 9Eine Verdopplung von n erhöht die Schrittzahl nur um eine Konstante - typisch für die binäre Suche. Markiert: n = 64 mit etwa 6 Schritten.
Musterbeispiel

Master-Theorem auf Mergesort anwenden

Bestimme die Lösung der Rekurrenz T(n)=2T(n/2)+nT(n) = 2T(n/2) + nT(n)=2T(n/2)+n mit dem Master-Theorem.

  1. 01Parameter ablesen

    a=2a=2a=2, b=2b=2b=2, f(n)=nf(n)=nf(n)=n.

  2. 02Vergleichsgröße

    log⁡ba=log⁡22=1\log_{b} a = \log_{2} 2 = 1logb​a=log2​2=1; also nlog⁡ba=nn^{\log_{b}a} = nnlogb​a=n.

  3. 03Fall prüfen

    f(n)=n=Θ(n1)f(n)=n=\Theta(n^{1})f(n)=n=Θ(n1) -> Fall 2 des Master-Theorems.

  4. 04Lösung anwenden

    T(n)=Θ(nlog⁡ba⋅log⁡n)=Θ(nlog⁡n)T(n) = \Theta(n^{\log_{b} a} \cdot \log n) = \Theta(n \log n)T(n)=Θ(nlogb​a⋅logn)=Θ(nlogn).

Ergebnis: Mergesort hat asymptotische Laufzeit T(n)∈Θ(nlog⁡n)T(n) \in \Theta(n \log n)T(n)∈Θ(nlogn) im Worst, Average und Best Case.

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Big-O sagt dir, wie sich eine Laufzeit verhält, wenn du die Eingabegröße verdoppelst.

    Logarithmisches Wachstum O(log n) bei wachsendem n

    Logarithmisches Wachstum O(log n)Schaubild, Nullstellen bei x = 1, steigend, im Bereich x von 1 bis 10242004006008001000246810n = 646 SchritteSchritte (relativ)n
    Abb.Eine Verdopplung von n erhöht die Schrittzahl nur um eine Konstante - typisch für die binäre Suche. Markiert: n = 64 mit etwa 6 Schritten.
  2. 2

    Das Master-Theorem ist die schnellste Methode, Divide-and-Conquer-Rekurrenzen zu lösen.

  3. 3

    Achte darauf, dass Big-O nur asymptotisch gilt - für kleine Eingaben kann ein quadratischer Algorithmus schneller sein als ein log-linearer.

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Erläutere die Big-O-Notation und vergleiche die Laufzeitklassen O(1)O(1)O(1), O(log⁡n)O(\log n)O(logn), O(n)O(n)O(n), O(nlog⁡n)O(n \log n)O(nlogn) und O(n2)O(n^{2})O(n2) anhand konkreter Algorithmen. Diskutiere am Beispiel von Mergesort die Anwendung des Master-Theorems.

Maturafokus

  • Wende das Master-Theorem mit konkreten aaa, bbb und f(n)f(n)f(n) an und benenne, welcher der drei Fälle greift.
  • Lass konstante Faktoren und Terme niedrigerer Ordnung in der Big-O-Angabe weg.
  • Unterscheide Worst, Average und Best Case in jeder Aussage über Laufzeit.
  • Ordne die Wachstumsklassen: konstant, logarithmisch, linear, nlog⁡nn\log nnlogn, quadratisch, exponentiell.
  • Erwähne, dass Big-O erst für große nnn aussagekräftig ist - bei kleinen Eingaben entscheiden die Konstanten.

Typische Fehler

  • Konstanten bleiben in der Big-O-Angabe stehen; O(3n)O(3n)O(3n) ist O(n)O(n)O(n).
  • Das Master-Theorem wird auf Rekurrenzen angewandt, die nicht der Divide-and-Conquer-Form entsprechen.
  • Exponentielles Wachstum wird als praktisch beherrschbar eingestuft.
  • Big-O wird als exakte Laufzeit gelesen; es ist eine obere Schranke des Wachstums.
  • Speicherbedarf und Laufzeit werden vermengt; beide brauchen eigene Angaben.

§ 05

Aktive Wiederholung

Bestimme mit dem Master-Theorem die Lösung der Rekurrenzen T(n)=3T(n/2)+n2T(n)=3T(n/2)+n^{2}T(n)=3T(n/2)+n2 und T(n)=4T(n/2)+nT(n)=4T(n/2)+nT(n)=4T(n/2)+n. Wie lautet die Komplexität jeweils?

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: CLRS: Introduction to Algorithms, Kapitel 3 und 4 (MIT Press)

§ 06
§ 06

Greedy- und Dynamic-Programming-Verfahren#

~4 Min Lesezeit●●●VertiefungLPINF-Alg-5.1

Kernpunkte

Manche Optimierungsprobleme lassen sich nicht durch blindes Ausprobieren lösen, weil es zu viele Möglichkeiten gibt. Zwei mächtige Entwurfsstrategien helfen: Greedy und Dynamic Programming. Sie unterscheiden sich darin, ob eine lokal beste Entscheidung schon global optimal ist.
Greedy-Algorithmen treffen in jedem Schritt die lokal beste Wahl und revidieren sie nie. Das ist einfach und schnell, aber nur garantiert optimal, wenn das Problem die passende Struktur (ein Matroid) hat. Erfolgreiche Beispiele sind die Aktivitätenauswahl, Huffman-Codes, minimale Spannbäume (Prim, Kruskal) und Coin Change in kanonischen Münzsystemen.
Beim Coin Change sieht man die Grenze von Greedy: Münzen {1, 3, 4}\{1,\,3,\,4\}{1,3,4}, Ziel 666 - Greedy nimmt 4+1+14+1+14+1+1 (3 Münzen), optimal sind 3+33+33+3 (2 Münzen). Lokal optimal ist eben nicht immer global optimal; genau so ein Gegenbeispiel sollte man in der Prüfung parat haben.
Dynamic Programming (DP) löst solche Fälle, indem es das Problem in überlappende Teilprobleme zerlegt und jedes nur einmal berechnet (die Ergebnisse werden gespeichert). Voraussetzung sind zwei Eigenschaften: optimale Substruktur (die Gesamtlösung setzt sich aus optimalen Teillösungen zusammen) und überlappende Teilprobleme (dieselben Teilprobleme treten mehrfach auf).
Es gibt zwei Bauweisen: Top-down (Memoization) - die rekursive Lösung mit Cache - und Bottom-up (Tabelle) - die Teilprobleme in passender Reihenfolge iterativ füllen. Beim 0/1-Rucksack mit der Rekurrenz K(i,w)=max⁡(K(i−1,w), K(i−1,w−wi)+vi)K(i,w)=\max\bigl(K(i-1,w),\,K(i-1,w-w_{i})+v_{i}\bigr)K(i,w)=max(K(i−1,w),K(i−1,w−wi​)+vi​) füllt sich die DP-Tabelle zeilenweise (); das Ergebnis K(3,50)=220K(3, 50)=220K(3,50)=220 steht unten rechts.

Knapsack-DP-Tabelle (W=50)

Knapsack-DP-TabelleTabelle mit 7 Spalten und 4 Zeilen, Daten: i / W · 0 · 10 · 20 · 30 · 40 · 50; 0 · 0 · 0 · 0 · 0 · 0 · 0; 1 · 0 · 60 · 60 · 60 · 60 · 60; 2 · 0 · 60 · 100 · 160 · 160 · 160; 3 · 0 · 60 · 100 · 160 · 180 · 220, hervorgehobene Zelle: 220i / W01020304050000000010606060606020601001601601603060100160180220
Abb. 10Bottom-up gefüllte DP-Tabelle für Werte [60,100,120] und Gewichte [10,20,30]; das Optimum 220 steht unten rechts.
Abb. 10 ↓
Die Laufzeit von DP ergibt sich aus der Tabellengröße mal Aufwand pro Zelle: O(n⋅W)O(n\cdot W)O(n⋅W) für 0/1-Knapsack - pseudopolynomiell, weil WWW als Zahlwert und nicht als Eingabelänge zählt. Klassische DP-Probleme sind Fibonacci, das Rucksackproblem, kürzeste Pfade (Bellman-Ford) und die Edit Distance. Häufige Fehler: Greedy auf nicht-kanonische Systeme anwenden, die Tabelle in falscher Reihenfolge füllen, oder Top-down ohne Memoization als DP ausgeben (dann wieder exponentiell).

Vokabeln

→ Kartei
  • Greedy-VerfahrenTrifft in jedem Schritt die lokal beste Wahl.
  • optimale TeilstrukturDie optimale Gesamtlösung enthält optimale Teillösungen.
  • überlappende TeilproblemeDieselben Teilprobleme treten mehrfach auf; sie lohnen die Zwischenspeicherung.
  • dynamische ProgrammierungSystematisches Lösen und Speichern aller Teilprobleme.
  • RucksackproblemAuswahl von Gegenständen unter Gewichtsgrenze mit maximalem Wert.
  • pseudopolynomiellLaufzeit polynomiell im Zahlenwert der Eingabe, nicht in ihrer Länge.

DP-Rekurrenz für 0/1-Knapsack

K(i,w)=max⁡(K(i−1,w), K(i−1,w−wi)+vi)K(i, w) = \max\bigl(K(i-1, w),\, K(i-1, w - w_{i}) + v_{i}\bigr)K(i,w)=max(K(i−1,w),K(i−1,w−wi​)+vi​)

K(i,w)K(i,w)K(i,w) ist der beste Wert mit den ersten iii Gegenständen bei Restkapazität www; wiw_{i}wi​ und viv_{i}vi​ sind Gewicht und Wert des iii-ten. Die beiden Argumente des Maximums sind die Entscheidungen weglassen und einpacken - der zweite Fall ist nur zulässig, wenn wi≤ww_{i}\le wwi​≤w gilt.

Musterbeispiel

0/1-Knapsack via DP

Werte [60,100,120][60, 100, 120][60,100,120], Gewichte [10,20,30][10, 20, 30][10,20,30], Kapazität W=50W=50W=50.

  1. 01DP-Tabelle initialisieren

    Tabelle KKK der Größe 4×514 \times 514×51; erste Zeile/Spalte = 0.

  2. 02Befüllen

    Für jedes Item iii und jede Kapazität www: K(i,w)=max⁡(K(i−1,w),K(i−1,w−wi)+vi)K(i,w) = \max(K(i-1,w), K(i-1, w-w_{i}) + v_{i})K(i,w)=max(K(i−1,w),K(i−1,w−wi​)+vi​), falls w≥wiw \geq w_{i}w≥wi​.

  3. 03Endergebnis ablesen

    K(3,50)=220K(3, 50) = 220K(3,50)=220 (Items 2 und 3).

Ergebnis: Maximaler Wert 220 EUR. Mit Backtracking auf der Tabelle lässt sich die optimale Auswahl rekonstruieren. Laufzeit O(n⋅W)O(n \cdot W)O(n⋅W) - pseudo-polynomiell.

Schritt-für-Schritt Erklärung3 Schritte
  1. 1

    Greedy ist verführerisch einfach - aber nur bei matroidischen Problemen garantiert optimal.

  2. 2

    DP ist die Klassiker-Antwort, wenn ein Problem optimale Teilstruktur und Überlappung hat.

  3. 3

    Memoization spart Zeit, DP-Tabelle spart Stack - wähle nach Problem.

SRDP-Aufgaben

SelbsttestAus der Fragenbank7 Punkte

Aufgabenstellung

Vergleiche Greedy-Algorithmen und Dynamic Programming. Erkläre an einem konkreten Beispiel, warum Greedy nicht immer das Optimum liefert.

Maturafokus

  • Prüfe vor jedem Greedy-Ansatz, ob das Problem die Greedy-Wahl-Eigenschaft besitzt - sonst liefert er nur eine Näherung.
  • Fülle die DP-Tabelle für Coin Change oder das Rucksackproblem zeilenweise aus und lies die Lösung am Ende ab.
  • Leite die Komplexität her: O(n⋅W)O(n\cdot W)O(n⋅W) beim 0/1-Rucksack ist pseudopolynomiell, weil WWW als Zahl und nicht als Eingabelänge eingeht.
  • Benenne die beiden DP-Voraussetzungen: optimale Teilstruktur und überlappende Teilprobleme.
  • Rekonstruiere zusätzlich die LÖSUNG, nicht nur den optimalen Wert - dafür wird die Tabelle rückwärts gelesen.

Typische Fehler

  • Greedy wird auf ein nicht kanonisches Münzsystem angewandt: Mit 1, 3 und 4 liefert Greedy für 6 die Zerlegung 4+1+14+1+14+1+1, optimal wären 3+33+33+3.
  • Die DP-Tabelle wird in falscher Reihenfolge gefüllt; benötigte Teilergebnisse fehlen dann noch.
  • Top-down-Rekursion ohne Memoization wird als dynamische Programmierung ausgegeben.
  • Nur der optimale Wert wird angegeben, obwohl nach der Zusammensetzung gefragt ist.
  • Pseudopolynomielle Laufzeit wird als polynomiell bezeichnet.

§ 06

Aktive Wiederholung

Löse das 0/1-Knapsack-Problem mit Werten [60,100,120][60, 100, 120][60,100,120], Gewichten [10,20,30][10, 20, 30][10,20,30] und Kapazität 505050 mittels DP-Tabelle.

Passende Aufgaben üben50 Fragen zum Thema→

Aktiv abrufen

Erinnere dich an die Kernpunkte — dann aufdecken.

Quellen: CLRS Kapitel 15 (DP) und 16 (Greedy) (MIT Press)

Stand 06/2026 · Vollständige Fassung über den Tiefenregler — gleiche Stelle, gleiche Anker

Inhalt

Abschnitt -- / 06

    • 01Kontrollstrukturen, Funktionen und Modularisierung○
    • 02Rekursion und Iteration◐
    • 03Sortierverfahren - Bubble, Insertion, Merge, Quicksort◐
    • 04Suchverfahren - linear und binaer○
    • 05Big-O, Master-Theorem und Grenzen●
    • 06Greedy- und Dynamic-Programming-Verfahren●

0/6 Gelesen

Aus den Notizen ins Training

INF-Alg - Algorithmen, Programmierung und Komplexität

Festige dieses Thema an passenden Aufgaben aus der Fragenbank.

~23
Min
4
Kompetenzen
50
Fragen
Üben
Beispielfrage

Erkläre am Beispiel einer selbst gewählten Aufgabe die Bedeutung der drei elementaren Kontrollstrukturen Sequenz, Selektion und Iteration. Schreibe die Lösung in Pseudocode oder einer Programmiersprache deiner Wahl.

7 BE · 2022

Zur Fragenbank
Wiederholung planen

Belege & Quellen

Quellen

CACM

  • Böhm/Jacopini: Flow diagrams, Turing machines and languages with only two formation rules

PSF

  • Python Software Foundation - Tutorial

MIT Press

  • Cormen, Leiserson, Rivest, Stein: Introduction to Algorithms (CLRS) Kap. 2-4

Addison-Wesley

  • Knuth: The Art of Computer Programming, Vol. 3 - Sorting and Searching
  • Sedgewick & Wayne: Algorithms, 4. Auflage

Virginia Tech

  • OpenDSA - Sorting Algorithms

Siehe auch

  • INF-DS - DatenstrukturenJeder Algorithmus wird erst durch die passende Datenstruktur effizient.
  • INF-Th - Theoretische InformatikKomplexitätsklassen und Berechenbarkeit setzen die Grenzen für jeden Algorithmus.
  • INF-Modell - Boolesche Logik, UML und ModellierungPseudocode, Flussdiagramm und Schleifeninvariante sind die Werkzeuge der Beschreibung.

Vorheriges Thema

INF-Daten - Informationssysteme, Codierung und Zahlensysteme

Nächstes Thema

INF-DS - Datenstrukturen

EuraStudy·Notizen T·02·MMXXVI

Weiter mit dem nächsten Thema — der Lernpfad bleibt erhalten.