🎯 Warum noch ein Sortierverfahren?
Starte dein BlueJ-Projekt und lass bubbleSort() laufen. Schau auf den Zähler oben rechts im Fenster:
Bei 8 Zahlen macht BubbleSort 28 Vergleiche. Bei 100 Zahlen wären es schon 4950. Bei einer Million Zahlen:
rund 500 Milliarden. Das dauert selbst für einen Computer spürbar lange.
So arbeiten BubbleSort und SelectionSort
Sie vergleichen stur fast jede Zahl mit fast jeder anderen. Doppelt so viele Zahlen bedeutet ungefähr viermal so viel Arbeit.
So arbeitet QuickSort
Er zerlegt die Liste clever in zwei Hälften, die sich gegenseitig nie wieder anschauen müssen. Das spart enorm viele Vergleiche.
🃏 Das Pivot-Spiel
+30 XP · Abzeichen 🃏Stell dir eine Schulklasse vor, die sich der Größe nach aufstellen soll. Eine Person wird zur Vergleichsperson erklärt, in der Informatik heißt sie Pivot. Alle anderen stellen sich links von ihr auf, wenn sie kleiner sind, sonst rechts. Danach steht das Pivot garantiert schon an seinem endgültigen Platz!
Deine Aufgabe: Verteile die Zahlen. Die erste Zahl der Liste ist das Pivot. Entscheide für jede weitere Zahl: links oder rechts?
⬅️ kleiner als Pivot
größer oder gleich ➡️
🌳 Zerlegen, zerlegen, zerlegen: Rekursion
+20 XPSo sieht das vollständige Zerlegen für eine Beispielliste aus. Gelb ist jeweils das Pivot. Jede Zeile zeigt eine Zerleg-Runde tiefer:
🔁 Das neue Werkzeug: Rekursion
Bei BubbleSort hast du Schleifen benutzt: wiederhole etwas mehrmals. QuickSort braucht zusätzlich Rekursion: eine Methode ruft sich selbst auf, aber jedes Mal mit einer kleineren Aufgabe. Damit das nicht ewig weitergeht, gibt es eine Abbruchregel:
Rekursiver Fall
Der Bereich hat mehr als eine Zahl → zerlege ihn am Pivot und rufe die Methode für beide Teilbereiche erneut auf.
Abbruchfall
Der Bereich hat nur noch eine Zahl (oder gar keine) → nichts zu tun, eine einzelne Zahl ist immer sortiert.
⚙️ Der Zeiger-Simulator
+20 XP · Abzeichen ⚙️
In deinem Java-Programm steckt die Liste in einem einzigen Feld zahlen.
Es gibt also keine echten Teillisten, das Zerlegen passiert durch Tauschen innerhalb des Feldes.
Dafür laufen zwei Zeiger aufeinander zu:
🔴 Zeiger links und rechts
links wandert nach rechts, bis er eine Zahl findet, die nicht kleiner als das Pivot ist.
rechts wandert nach links, bis er eine Zahl findet, die nicht größer ist.
Beide Fundstücke stehen falsch → tauschen!
🟡 Das Pivot
Der gelbe Balken zeigt, wo das Pivot am Anfang des Bereichs stand. Sobald sich die Zeiger gekreuzt haben, ist der Bereich zerlegt und die Rekursion übernimmt die beiden Teile.
🤔 Beobachtungsaufgabe (für Schnelle)
Lass den Simulator mehrmals mit neuen Zahlen laufen und beantworte in deinem Heft:
a) Woran erkennst du im Ablauf, dass ein neuer, kleinerer Bereich begonnen wurde?
b) Wie tief wurde die Rekursion maximal? Wovon hängt das ab?
c) Warum dürfen die Zeiger beim Pivot selbst stehen bleiben, ohne dass etwas kaputtgeht?
🏁 Das Wettrennen: BubbleSort gegen QuickSort
+20 XP · Abzeichen 🏁Beide Verfahren bekommen exakt dieselben 12 Zahlen. Wer zuerst fertig ist, gewinnt. Tipp: Schau am Ende auf die Vergleichszähler, nicht nur auf die Ziellinie.
🫧 BubbleSort
⚡ QuickSort
☕ Von Python zu Java übersetzen
+20 XP · Abzeichen ☕
Auf inf-schule hast du QuickSort mit einer Liste und den Indexgrenzen anfang und ende in Python gesehen.
Genau diese Variante baust du jetzt in Java nach, denn sie passt perfekt zu deinem Feld zahlen:
def quicksort(L, anfang, ende): if L != []: pivot = L[anfang] links = anfang rechts = ende while links <= rechts: while L[links] < pivot: links = links + 1 while L[rechts] > pivot: rechts = rechts - 1 if links <= rechts: if links < rechts: h = L[links] L[links] = L[rechts] L[rechts] = h links = links + 1 rechts = rechts - 1 if anfang < rechts: L = quicksort(L, anfang, rechts) if links < ende: L = quicksort(L, links, ende) return L
🔁 Das Übersetzungs-Wörterbuch
Vieles kennst du schon aus deiner Klasse Main. Die wichtigsten Übersetzungen:
| Python (inf-schule) | Java (dein BlueJ-Projekt) | Bemerkung |
|---|---|---|
def quicksort(L, anfang, ende): | private void sortiereBereich(int anfang, int ende) | Das Feld zahlen gehört zum Objekt, es muss nicht übergeben oder zurückgegeben werden. |
pivot = L[anfang] | pivot = zahlen[anfang]; | Variablen vorher deklarieren: int pivot; |
while links <= rechts: | while(links <= rechts) { ... } | Java: Klammern und geschweifte Klammern statt Einrückung. |
h = L[links] usw. (3 Zeilen) | tausche(links, rechts); | Den Dreieckstausch hast du schon als fertige Methode! 🎉 |
L = quicksort(L, anfang, rechts) | sortiereBereich(anfang, rechts); | Selbstaufruf der Methode = Rekursion. |
while L[rechts] > pivot: in deinem Java-Programm?🧩 Programmierauftrag: quickSort() einbauen
Du erweiterst deine bestehende Klasse Main. Nichts löschen!
BubbleSort und SelectionSort bleiben drin, du brauchst sie später noch für den Vergleich.
Main.selectionSort() ein (vor der Methode tausche).sortiereBereich(...) mit Hilfe der TODOs, des Python-Codes und des Übersetzungs-Wörterbuchs.neueZahlen(), dann quickSort(). Beobachte Pivot 🟡 und Zeiger 🔴.neueZahlen() und danach bubbleSort() auf und vergleiche die Zählerstände. 📊/** * QuickSort: Startdienst. * Setzt die Zaehler zurueck und sortiert das ganze Feld * von Index 0 bis Index anzahl - 1. */ public void quickSort() { vergleiche = 0; tausche = 0; zeichne("QuickSort", -1, -1, -1, -1, "Zerlegen mit Pivot. Los geht es!"); uhr.warte(900); sortiereBereich(0, anzahl - 1); zeichne("QuickSort fertig", -1, -1, -1, 0, "Fertig! Rufe neueZahlen() und bubbleSort() auf und vergleiche."); } /** * AUFGABE: Sortiert den Bereich von anfang bis ende mit QuickSort. * Diese Methode ruft sich selbst auf (Rekursion). */ private void sortiereBereich(int anfang, int ende) { int pivot; int links; int rechts; // TODO 1: Pivot und Zeiger setzen // pivot = die Zahl an der Stelle anfang // links = anfang // rechts = ende // TODO 2: Zerlegen, solange links <= rechts ist: // a) links wandert nach rechts (links = links + 1), // solange zahlen[links] kleiner als pivot ist // b) rechts wandert nach links (rechts = rechts - 1), // solange zahlen[rechts] groesser als pivot ist // c) wenn links <= rechts: // wenn links < rechts: tausche(links, rechts) // danach: links um 1 erhoehen, rechts um 1 verringern // TODO 3: Rekursion fuer die beiden Teilbereiche: // wenn anfang < rechts: sortiereBereich(anfang, rechts) // wenn links < ende: sortiereBereich(links, ende) // Tipp fuer die Anzeige zwischendurch: // zeichne("QuickSort", links, rechts, anfang, -1, "..."); // uhr.warte(700); // Der dritte Wert (anfang) faerbt das Pivot gelb, // links und rechts werden rot markiert. }
< bzw. >,
nicht <= oder >=, sonst rennen die Zeiger aus dem Feld
(Fehlermeldung ArrayIndexOutOfBoundsException). Und: Bei der Rekursion unbedingt sortiereBereich
aufrufen, nicht quickSort, sonst beginnt alles von vorn.
🛟 Gestufte Hilfen
Erst nachdenken, dann Simulator anschauen, dann Hilfe 1, dann Hilfe 2 ... Öffne immer nur so viel, wie du wirklich brauchst.
🥉 Hilfe 1: TODO 1, Pivot und Zeiger
pivot = zahlen[anfang]; links = anfang; rechts = ende; zeichne("QuickSort: neuer Bereich", -1, -1, anfang, -1, "Pivot ist " + pivot + " (gelb)."); uhr.warte(800);
Das Pivot ist eine Zahl (ein Wert), links und rechts sind Stellen (Indizes). Nicht verwechseln!
🥈 Hilfe 2: TODO 2, die Zerlege-Schleife
while(links <= rechts) { while(zahlen[links] < pivot) { links = links + 1; } while(zahlen[rechts] > pivot) { rechts = rechts - 1; } if(links <= rechts) { if(links < rechts) { tausche(links, rechts); } links = links + 1; rechts = rechts - 1; } }
Baue zwischen den Schritten zeichne(...) und uhr.warte(700) ein, damit du den Ablauf siehst.
Vergiss die Zähler nicht: vergleiche = vergleiche + 1; in den inneren Schleifen,
tausche = tausche + 1; direkt nach jedem Tausch.
🥇 Hilfe 3: TODO 3, die Rekursion
if(anfang < rechts) { sortiereBereich(anfang, rechts); } if(links < ende) { sortiereBereich(links, ende); }
Die beiden if-Abfragen sind der Abbruchfall: Ist ein Teilbereich leer oder hat nur eine Zahl, wird gar nicht mehr aufgerufen. Die Rekursion endet von selbst.
🚨 Notfall-Hilfe: komplette Musterlösung (wirklich nur im Notfall!)
private void sortiereBereich(int anfang, int ende) { int pivot; int links; int rechts; pivot = zahlen[anfang]; links = anfang; rechts = ende; zeichne("QuickSort: neuer Bereich", -1, -1, anfang, -1, "Bereich " + anfang + " bis " + ende + ". Pivot ist " + pivot + "."); uhr.warte(800); while(links <= rechts) { while(zahlen[links] < pivot) { vergleiche = vergleiche + 1; links = links + 1; } vergleiche = vergleiche + 1; while(zahlen[rechts] > pivot) { vergleiche = vergleiche + 1; rechts = rechts - 1; } vergleiche = vergleiche + 1; zeichne("QuickSort: Zeiger gestoppt", links, rechts, anfang, -1, "links und rechts haben passende Zahlen gefunden."); uhr.warte(700); if(links <= rechts) { if(links < rechts) { tausche(links, rechts); tausche = tausche + 1; zeichne("QuickSort: Tausch", links, rechts, anfang, -1, "Das falsche Paar wurde getauscht."); uhr.warte(700); } links = links + 1; rechts = rechts - 1; } } if(anfang < rechts) { sortiereBereich(anfang, rechts); } if(links < ende) { sortiereBereich(links, ende); } }
Wenn du die Lösung kopierst: Gehe sie Zeile für Zeile durch und schreibe an jeden Block einen eigenen Kommentar, was er tut. In der nächsten Stunde erklärst du den Ablauf ohne Spickzettel. 😉
🚀 Bonus-Missionen
Fertig und alles läuft? Such dir eine Mission aus. Reihenfolge egal, Schwierigkeit steigt mit den Sternen.
⭐ Mission A: Der große Zahlenvergleich
Erstelle eine Tabelle im Heft: Lass bubbleSort(), selectionSort() und quickSort()
je dreimal mit neuen Zahlen laufen und notiere Vergleiche und Tausche. Welches Verfahren gewinnt in welcher Kategorie?
⭐ Mission B: Mehr Balken!
Setze im Konstruktor anzahl = 12;. Damit alles ins Fenster passt, musst du auch
balkenBreite und luecke anpassen. Probiere passende Werte aus. Wird der Vorsprung von QuickSort größer?
⭐⭐ Mission C: Der fiese Spezialfall
Schreibe eine Methode sortierteZahlen(), die das Feld mit bereits aufsteigend sortierten Werten füllt
(z. B. 10, 20, 30, ...). Lass dann quickSort() laufen. Beobachte die Zerlegung: Warum ist das erste Element
als Pivot hier eine schlechte Wahl?
⭐⭐⭐ Mission D: Das Zufalls-Pivot
Profis wählen das Pivot zufällig: Ziehe mit rechner.ganzeZufallsZahl(anfang, ende) eine Stelle,
tausche sie zuerst an die Position anfang und mache dann normal weiter.
Hilft das beim Spezialfall aus Mission C? Teste es!
🏆 Abschluss-Check
+40 XP · Abzeichen 🏆Hake ehrlich ab. Erst wenn alles sitzt, gibt es den Meistertitel.
Tipp: 1000 → 500 → 250 → 125 → ... Zähle die Halbierungen.