Vergleich A: Binäre Suche
Iterative Darstellung
int binarySearchIter(int[] a, int key) {
int l = 0, r = a.length - 1;
while (l <= r) {
int m = (l + r) / 2;
if (a[m] == key) return m;
if (key < a[m]) r = m - 1;
else l = m + 1;
}
return -1;
}
Struktur (iterativ)
Initialisierung: l, r
WHILE l ≤ r
├─ m berechnen
├─ Treffer? → return
└─ Intervall halbieren
Rekursive Darstellung
int binarySearchRec(int[] a, int key, int l, int r) {
if (l > r) return -1;
int m = (l + r) / 2;
if (a[m] == key) return m;
if (key < a[m]) return binarySearchRec(a, key, l, m - 1);
return binarySearchRec(a, key, m + 1, r);
}
Struktur (rekursiv)
Anker: l > r → -1
m berechnen
Treffer? → m
sonst rekursiver Aufruf
[0..15]
→ [8..15]
→ [12..15]
→ [12..13]
→ Treffer/Abbruch
Intervallfolge iterativ (gleiche Eingabe)
Start: l=0, r=15
Schleife 1: m=7 → rechte Hälfte ⇒ l=8, r=15
Schleife 2: m=11 → rechte Hälfte ⇒ l=12, r=15
Schleife 3: m=13 → linke Hälfte ⇒ l=12, r=12
Schleife 4: m=12 → Treffer oder Abbruch
Intervallfolge rekursiv (gleiche Eingabe)
binarySearchRec(a,key,0,15)
→ Teilproblem entsteht: binarySearchRec(a,key,8,15)
→ Teilproblem entsteht: binarySearchRec(a,key,12,15)
→ Teilproblem entsteht: binarySearchRec(a,key,12,12)
→ Basisfall/Treffer: Rückgabe in umgekehrter Reihenfolge
| Aspekt |
Iteration |
Rekursion |
| Steuerung |
Explizit über Schleifenbedingungen. |
Implizit über Selbstaufrufe + Anker. |
| Zustand |
Variablen wie l, r, i werden fortgeschrieben. |
Zustand steckt in Parametern jedes Aufrufs. |
| Struktur |
Linearer Kontrollfluss im selben Block. |
Hierarchische Zerlegung in Teilprobleme. |
| Denkweise |
„Wiederhole, bis Bedingung verletzt.“ |
„Löse kleineres Teilproblem und kombiniere.“ |
| Speicher |
Konstanter Zusatzspeicher (typisch). |
Zusätzlicher Aufrufstack pro Rekursionsstufe. |
| Konkretes Beispiel (binäre Suche) |
Ein Schleifendurchlauf ersetzt den vorherigen Zustand (l,r). |
Jeder Aufruf erzeugt einen neuen Zustand als eigenes Teilproblem. |
Rekursion versus Iteration
Rekursion und Iteration sind zwei verschiedene Wege für wiederholte
Problemlösungen: Iteration steuert Wiederholung über Schleifen, Rekursion über Aufrufe auf kleineren Teilproblemen.
Welche Variante günstiger ist, hängt von Problemstruktur, Lesbarkeit und Aufwand ab.
Konkreter Vergleich (binäre Suche): Beide Varianten halbieren denselben Suchraum.
Iterativ ändern sich nur l/r in einer Schleife, rekursiv entstehen nacheinander
Aufrufe wie (l,r)=(0,15)→(8,15)→(12,15).
Vergleich B: Sortierstrategie
| Problem / Idee |
Iteratives Referenzmodell |
Rekursives Modell |
Fachliche Auswertung |
| Unsortierte Liste ordnen |
Insertion Sort: baue von links einen sortierten Bereich auf;
jedes Element wird durch Vergleiche eingeordnet.
|
Quicksort: teile per Pivot in „kleiner“ und „größer“,
sortiere Teilfolgen rekursiv, füge logisch zusammen.
|
Insertion ist lokal einfach, bei großen Datenmengen oft langsam.
Quicksort nutzt Teile-und-herrsche, ist typischerweise deutlich schneller,
kann im ungünstigen Fall aber stark abfallen.
|
Insertion: struktogrammnahe Blockfolge
FOR i = 1 bis n-1
├─ x = a[i]
├─ WHILE a[j] > x: verschiebe
└─ x einfügen
Quicksort: vollständiges Beispiel (rekursiv)
Ausgangsliste: [7 2 9 4 1 8 3]
Ebene 0: Pivot 4 → [2 1 3] |4| [7 9 8]
Teilprobleme entstehen: sort([2 1 3]) und sort([7 9 8])
Ebene 1 links: Pivot 1 → [] |1| [2 3]
Ebene 1 rechts: Pivot 8 → [7] |8| [9]
Ebene 2 links-rechts: Pivot 2 → [] |2| [3]
Basisfälle: [] , [3] , [7] , [9]
Kombination: [1 2 3] |4| [7 8 9]
Ergebnis: [1 2 3 4 7 8 9]
Quicksort in Java (Teilproblem + Rekursion explizit)
void quicksort(int[] a, int l, int r) {
if (l >= r) return; // Basisfall
int p = partition(a, l, r); // Zerlegung um Pivot
quicksort(a, l, p - 1); // Teilproblem links
quicksort(a, p + 1, r); // Teilproblem rechts
}
Rekursionsbaum (Beispiel [7 2 9 4 1 8 3])
sort([7 2 9 4 1 8 3])
├─ sort([2 1 3])
│ ├─ sort([])
│ └─ sort([2 3])
│ ├─ sort([])
│ └─ sort([3])
└─ sort([7 9 8])
├─ sort([7])
└─ sort([9])