In Vorlesung 6 habt ihr Rekursion und den ersten O(n log n)-Sortierer Merge Sort kennengelernt. Heute setzt ihr beides selbst um â und seht den Quantensprung von O(nÂČ) zu O(n log n) live.
Die Aufgaben bauen aufeinander auf: zuerst Rekursion verstehen (FakultĂ€t, Fibonacci), dann Merge Sort schreiben, dann das Trace-Werkzeug ĂŒben. Wer schneller fertig ist, baut den Klassiker Tower of Hanoi (Bonus).
Die Algorithmen sind sprachunabhĂ€ngig. Implementiert sie in C (Standard), Python, Java, JavaScript, Rust, Arduino-C++ â was ihr am liebsten nutzt. Was zĂ€hlt: der Algorithmus arbeitet korrekt und ihr versteht jeden Schritt.
Musterlösungen liegen nur in C vor. Wenn ihr in einer anderen Sprache arbeitet â ĂŒbersetzt die Logik, die Syntax Ă€ndert sich, der Algorithmus nicht. Passwörter sind ganz unten frei zugĂ€nglich.
Funktionen, die sich selbst aufrufen â Basis-Fall + Verkleinerung â und der Call-Stack im Kopf nachvollziehen
Aufteilen + Verschmelzen-Trick selbst implementieren â O(n log n) garantiert
Klassiker wie Tower of Hanoi â wo Rekursion die Lösung elegant macht
Eine Rekursion auf Papier durchspielen â sehen, wie die Aufrufe runter und Werte hoch wandern
Ziel: die einfachste Rekursion selbst schreiben â und den Call-Stack im Kopf nachvollziehen.
n! = 1 · 2 · 3 · ⊠· n. Rekursiv: n! = n · (n-1)!, mit 1! = 1 als Basis-Fall.
fakt(n), die n! berechnet.fakt(4) â push-Phase, dann RĂŒckgabe-Phase mit den Multiplikationen.int ĂŒber (12! = 479 001 600 passt gerade, 13! nicht mehr). Nimm long long fĂŒr gröĂere Werte â oder bleib bis n=12, das ist fĂŒr die Ăbung sowieso ausreichend.
if n <= 1 return 1)? Probier's aus â und sei bereit, das Programm mit Ctrl+C abzubrechen.
#include <stdio.h>
long long fakt(int n) {
if (n <= 1) {
return 1; // Basis-Fall
}
return (long long)n * fakt(n - 1); // rekursiver Schritt
}
int main(void) {
for (int n = 1; n <= 10; n++) {
printf("%2d! = %lld\n", n, fakt(n));
}
return 0;
}
Erwartete Ausgabe:
1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 ... 10! = 3628800
Ziel: ein Algorithmus, zwei Implementierungen â und sehen, warum die naive Rekursion bei groĂen n unbenutzbar ist.
fib(0) = 0, fib(1) = 1, fib(n) = fib(n-1) + fib(n-2).
fib_rek(n) ruft sich zweimal selbst auf.fib_iter(n) mit einer for-Schleife und zwei Variablen.clock() aus <time.h>.Die iterative Version ist instant â auch bei n = 1000. Die rekursive Version explodiert nach n = 40: jeder Schritt mehr verdoppelt die Zeit. Warum?
fib_rek(50) â das dauert Minuten. Geht schrittweise hoch.
#include <stdio.h>
#include <time.h>
// Naive Rekursion â O(2^n)
long long fib_rek(int n) {
if (n < 2) return n;
return fib_rek(n - 1) + fib_rek(n - 2);
}
// Iteration â O(n)
long long fib_iter(int n) {
if (n < 2) return n;
long long a = 0, b = 1, c;
for (int i = 2; i <= n; i++) {
c = a + b;
a = b;
b = c;
}
return b;
}
void messung(int n) {
clock_t start, ende;
double ms;
long long ergebnis;
start = clock();
ergebnis = fib_iter(n);
ende = clock();
ms = (double)(ende - start) / CLOCKS_PER_SEC * 1000.0;
printf("n=%2d iter: %lld (%.3f ms)\n", n, ergebnis, ms);
start = clock();
ergebnis = fib_rek(n);
ende = clock();
ms = (double)(ende - start) / CLOCKS_PER_SEC * 1000.0;
printf(" rek: %lld (%.3f ms)\n", ergebnis, ms);
printf("\n");
}
int main(void) {
int werte[] = {10, 20, 30, 35, 40};
for (int i = 0; i < 5; i++) {
messung(werte[i]);
}
return 0;
}
Beobachtung (typisch):
n=10 iter: 55 (0.000 ms)
rek: 55 (0.001 ms)
n=30 iter: 832040 (0.000 ms)
rek: 832040 (3.4 ms)
n=40 iter: 102334155 (0.000 ms)
rek: 102334155 (420 ms) â 100 000Ă langsamer!
Ziel: den ersten O(n log n)-Sortierer von Hand implementieren â und live sehen, wie viel schneller er als Bubble Sort ist.
merge(a, l, m, r) â verschmilzt zwei sortierte Teile a[l..m] und a[m+1..r] in einen sortierten Teil.merge_sort(a, l, r) â Basis-Fall: l >= r. Sonst: linke HĂ€lfte rekursiv sortieren, rechte rekursiv, dann verschmelzen.[5, 2, 8, 1, 9, 3, 7, 4] â erwartet: [1, 2, 3, 4, 5, 7, 8, 9].int hilfs[1024];) fĂŒr die ersten Tests. FĂŒr richtige Skalierung könnt ihr spĂ€ter auf malloc umsteigen â aber fĂŒr n †1024 reicht statisch.
#include <stdio.h>
void merge(int a[], int l, int m, int r) {
int hilfs[1024];
int i = l, j = m + 1, k = 0;
while (i <= m && j <= r) {
if (a[i] <= a[j]) { hilfs[k++] = a[i++]; }
else { hilfs[k++] = a[j++]; }
}
while (i <= m) hilfs[k++] = a[i++];
while (j <= r) hilfs[k++] = a[j++];
for (int x = 0; x < k; x++) {
a[l + x] = hilfs[x];
}
}
void merge_sort(int a[], int l, int r) {
if (l >= r) return;
int m = (l + r) / 2;
merge_sort(a, l, m);
merge_sort(a, m + 1, r);
merge(a, l, m, r);
}
void drucke(int a[], int n) {
for (int i = 0; i < n; i++) printf("%d ", a[i]);
printf("\n");
}
int main(void) {
int a[] = {5, 2, 8, 1, 9, 3, 7, 4};
int n = 8;
printf("vorher: "); drucke(a, n);
merge_sort(a, 0, n - 1);
printf("nachher: "); drucke(a, n);
return 0;
}
Erwartete Ausgabe:
vorher: 5 2 8 1 9 3 7 4 nachher: 1 2 3 4 5 7 8 9
Ziel: ein iteratives Problem rekursiv lösen â und sehen, wie elegant der Code wird.
Das Maximum eines Arrays a[0..n-1] ist entweder das letzte Element a[n-1], oder das Maximum aus dem Rest a[0..n-2]. Das ist eine rekursive Definition â daher die natĂŒrliche Lösung.
max_rek(int a[], int n).[3, 9, 1, 7, 5, 8, 2] â erwartet: 9.Ein Trace (deutsch: SpurfĂŒhrung) ist ein Papier-Durchspielen der Rekursion â Schritt fĂŒr Schritt, ohne den Computer. Das ist das Werkzeug, um Rekursion zu verstehen: man sieht die Aufrufe runtergehen und die Werte hochkommen.
So liest man einen Trace:
Beispiel-Trace fĂŒr a = [3, 9, 1, 7]:
max_rek([3,9,1,7], 4) â max(7, max_rek(..., 3)) // will Maximum von 7 und vom Rest max_rek([3,9,1], 3) â max(1, max_rek(..., 2)) // will Maximum von 1 und vom Rest max_rek([3,9], 2) â max(9, max_rek(..., 1)) // will Maximum von 9 und vom Rest max_rek([3], 1) â 3 // BASIS â gibt 3 zurĂŒck â 9 (= max(9, 3)) // jetzt max(9, 3) berechnen â 9 â 9 (= max(1, 9)) // max(1, 9) â 9 â 9 (= max(7, 9)) // max(7, 9) â 9 â Endergebnis
So baut ihr selbst einen Trace:
max_rek([3,9,1,7], 4)).Ăbung: macht denselben Trace fĂŒr a = [5, 1, 8, 2] auf Papier. Vergleicht zum Schluss mit eurem Code-Output.
#include <stdio.h>
int max_rek(int a[], int n) {
if (n == 1) {
return a[0]; // Basis-Fall
}
int rest_max = max_rek(a, n - 1); // Maximum aus a[0..n-2]
if (a[n - 1] > rest_max) {
return a[n - 1];
}
return rest_max;
}
int main(void) {
int a[] = {3, 9, 1, 7, 5, 8, 2};
int n = 7;
printf("Maximum: %d\n", max_rek(a, n)); // â 9
return 0;
}
Variante (kompakter mit Ternary-Operator):
int max_rek(int a[], int n) {
if (n == 1) return a[0];
int rest = max_rek(a, n - 1);
return (a[n - 1] > rest) ? a[n - 1] : rest;
}
Ziel: ein Klassiker â und der beste Beweis, wie elegant Rekursion sein kann. Gleiche Lösung in einer Schleife wĂ€re richtig hĂ€sslich.
Drei StĂ€be (A, B, C). Auf A stehen n Scheiben â gröĂte unten, kleinste oben. Ziel: alle Scheiben auf C bringen. Regeln:
Um n Scheiben von A nach C zu bewegen (mit B als Zwischen-Stab):
hanoi(n, von, nach, ueber)."Scheibe 1: A -> C").#include <stdio.h>
int zaehler = 0;
void hanoi(int n, char von, char nach, char ueber) {
if (n == 1) {
printf(" Scheibe 1: %c -> %c\n", von, nach);
zaehler++;
return;
}
hanoi(n - 1, von, ueber, nach); // n-1 aus dem Weg
printf(" Scheibe %d: %c -> %c\n", n, von, nach);
zaehler++;
hanoi(n - 1, ueber, nach, von); // n-1 drauf
}
int main(void) {
int n = 4;
printf("Hanoi mit %d Scheiben:\n", n);
hanoi(n, 'A', 'C', 'B');
printf("\nGesamt: %d ZĂŒge (2^%d - 1 = %d)\n",
zaehler, n, (1 << n) - 1);
return 0;
}
Erwartete Ausgabe (n=4):
Hanoi mit 4 Scheiben: Scheibe 1: A -> B Scheibe 2: A -> C Scheibe 1: B -> C Scheibe 3: A -> B Scheibe 1: C -> A Scheibe 2: C -> B Scheibe 1: A -> B Scheibe 4: A -> C Scheibe 1: B -> C Scheibe 2: B -> A Scheibe 1: C -> A Scheibe 3: B -> C Scheibe 1: A -> B Scheibe 2: A -> C Scheibe 1: B -> C Gesamt: 15 ZĂŒge (2^4 - 1 = 15)
merge() als auch merge_sort()Die Passwörter stehen unten â frei zugĂ€nglich. Bitte fair spielen: erst selbst implementieren, dann mit der Musterlösung vergleichen. Eine Lösung, die ihr nur abgeschrieben habt, hilft euch in der Klausur nicht.
| Aufgabe | Thema | Passwort |
|---|---|---|
| Aufgabe 1 | Rekursion â FakultĂ€t | fakultaet |
| Aufgabe 2 | Fibonacci â Rekursion vs. Iteration | fibonacci |
| Aufgabe 3 | Merge Sort | merge |
| Aufgabe 4 | Rekursives Maximum + Trace-Tutorial | maximum |
| Aufgabe 5 | Tower of Hanoi (Bonus) | hanoi |
Hinweis fĂŒr Python/Java/Rust-Lösungen: die Algorithmen sind 1:1 ĂŒbertragbar â ĂŒbersetzt nur die Syntax. Eigene Musterlösungen fĂŒr jede Sprache gibt es nicht.
© 2026 HTW Berlin · Prof. Dr. Alexandra Mikityuk