3 Sortieren

3.1 Direkte Sortiermethoden
3.2 Das Effizienz-Problem
3.3 Quicksort

Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel



Zu den häufigsten Aufgaben, die Computer zu erledigen haben, gehört das Sortieren von Daten nach gewissen vorgegebenen Kriterien. Man schätzt, dass etwa ein Viertel der weltweit investierten "CPU-Zeit" auf Sortieraufgaben entfällt. Speziell in Datenbanken muss häufig sortiert werden, und bei großen Datenmengen kommt es natürlich darauf an, dass dies möglichst schnell geschieht.



3.1 Direkte Sortiermethoden

Zum Studium verschiedener Sortieralgorithmen betrachten wir ein Array, das 16 zufällige Integer-Zahlen enthält. Diese sollen aufsteigend sortiert werden. Ein naheliegender Ansatz ist, die kleinste Zahl herauszusuchen und an die erste Stelle zu setzen, dann die zweitkleinste an die zweite Stelle usw. Das Problem dabei ist, dass noch unklar ist, wie man aus vielen Zahlen die kleinste herausfindet, wo wir doch mit dem Rechner stets nur 2 Zahlen miteinander vergleichen können. Dazu muss man eben mehrere Vergleiche "hintereinanderhängen":
Wir vergleichen a[0] mit a[1]; wenn a[0] > a[1] ist, dann werden die beiden Zahlen ausgetauscht;
wir vergleichen a[0] mit a[2]; wenn a[0] > a[2] ist, dann werden die beiden Zahlen ausgetauscht;
wir vergleichen a[0] mit a[3]; wenn a[0] > a[3] ist, dann werden die beiden Zahlen ausgetauscht;
..... (usw bis) .....
wir vergleichen a[0] mit a[15]; wenn a[0] > a[15] ist, dann werden die beiden Zahlen ausgetauscht.
Nun steht sicher in a[0] das kleinste der Elemente des Arrays. Jetzt suchen wir das zweitkleinste Element, d.h. das kleinste im Teilarray a[1..15]:
Wir vergleichen a[1] mit a[2]; wenn a[1] > a[2] ist, dann werden die beiden Zahlen ausgetauscht;
wir vergleichen a[1] mit a[3]; wenn a[1] > a[3] ist, dann werden die beiden Zahlen ausgetauscht;
..... (usw bis) .....
wir vergleichen a[1] mit a[15]; wenn a[1] > a[15] ist, dann werden die beiden Zahlen ausgetauscht.
Danach stimmen die ersten beiden Zahlen im (ganzen!) Array; wir können uns also der Sortierung des Teilarrays a[2..15] zuwenden, und dann a[3..15], a[4..15],..... bis schließlich a[14..15]. Dann ist das ganze Array sortiert.



Aufgaben:

  1. Sortieren durch direkten Austausch:

    Schreiben Sie ein Programm, das mit Hilfe des oben beschriebenen Algorithmus ein mit 16 zufälligen Integerzahlen gefülltes Array aufsteigend sortiert.

    In Delphi stellen Sie dazu zunächst eine entsprechende Integer-Array-Variable zur Verfügung, die im ganzen Programm bekannt ist: deklarieren Sie sie daher im private-Abschnitt der Formular-Deklaration. Den Inhalt des Array können Sie dann mit Hilfe einer Prozedur (z.B. "ZeigeTabelle()") in einer StringGrid-Komponente darstellen lassen. Sorgen Sie beim Programmstart für eine entsprechende Beschriftung des StringGrid sowie für die Füllung des Arrays mit Zufallszahlen (zwischen 0 und 100) und deren Anzeige.

    Für die Details des Umganges mit der nicht ganz einfach zu handhabenden Komponente "StringGrid" sollten Sie die Delphi-Hilfe zu Rate ziehen. Nahezu alle relevanten Einstellungen lassen sich über die im Objekt-Inspektor aufgeführten Eigenschaften einstellen.

    Zur Implementierung des Sortierungsalgorithmus ist es sinnvoll, eine Prozedur "tausche()" zur Verfügung zu stellen, die zwei übergebene Integerzahlen vertauscht. Dabei gibt es 2 Möglichkeiten für die Parameter: entweder Sie übergeben direkt die Zahlen oder Sie übergeben nur die Indizes der Zahlen im Array. Welche der Möglichkeiten Sie wählen, hat einen gravierenden Einfluß auf die nötige Parameterliste der Prozedur! Warum?

    Außerdem ist es im Hinblick auf spätere Untersuchungen der verschiedenen Sortieralgorithmen sinnvoll, auch den Vergleich zweier Array-Einträge auszulagern, und zwar in eine boolsche Funktion "vergleiche()" (oder auch "istGroesser()"). Auch hier haben Sie wieder die beiden obengenannten Möglichkeiten für die Wahl des Parameter-Designs. Diesmal ist die Sache aber nicht so kritisch. Warum?
    In Java stellen Sie dazu zunächst eine entsprechende Integer-Array-Variable zur Verfügung, in der dann der eigentliche Sortiervorgang stattfinden wird. Diese Variable sollte auf derselben logischen Ebene deklariert werden wie die visuellen Komponenten Ihres Formulars, damit sie nämlich (genau wie diese) im gesamten Programm bekannt ist. Den Inhalt dieses Arrays können Sie dann mit Hilfe einer Prozedur (z.B. "moveIntsFromArrayToTable()" ) in einer Zeile einer jTable-Komponente darstellen lassen. Mit der Eigenschaft jTable.Columns können Sie schon zur Programmierzeit für passende Spaltenüberschriften sorgen: tragen Sie dort die Spalten-Nummern (0..15) ein. Wenn Sie die Eigenschaft RowCount auf "2" setzen, dann zeigt die Vorschau des JAVA-Editors zwar unter der Kopfzeile fälschlicherweise nur eine(!) Datenzeile an; zur Laufzeit werden Sie aber neben der Kopfzeile (mit dem Index "0" und den eingetragenen Spalten-Nummern) zwei(!) zunächst leere Datenzeilen (mit den Indizes "1" und "2") vorfinden. Schreibenden Zugriff auf die Datenfelder der Tabelle gestattet Ihnen die Methode "jTable.setValueAt(String data, int x, int y)". Beachten Sie, dass die Tabelle nur Strings darstellen kann!

    Zur Implementierung des Sortier-Algorithmus ist es sinnvoll, eine Prozedur "switchData()" zur Verfügung zu stellen, die zwei übergebene Integerzahlen vertauscht. Dabei ist es sinnvoll, dieser Prozedur die Array-Indizes der beiden zu vergleichenden Zahlen zu übergeben. Außerdem ist es im Hinblick auf spätere Untersuchungen der verschiedenen Sortieralgorithmen sinnvoll, auch den Vergleich zweier Array-Einträge auszulagern, und zwar in eine boolsche Funktion "compareData()", der ebenfalls die Array-Indizes der zu vergleichenden Daten übergeben werden.

    Die eigentliche Sortierprozedur kann dann als Klick-Prozedur eines entsprechenden Knopfes formuliert werden. Sehen Sie zusätzlich auch noch einen Knopf zum erneuten Befüllen des Array mit "frischen" Zufallszahlen vor!

    Für die weiteren Forschungen sollten sie das Programm mitzählen lassen, wie viele Vergleiche und wieviele Tauschvorgänge durchgeführt werden mussten, und diese Anzahlen nach dem Sortieren ausgeben lassen.


  2. Bubble-Sort ist schon ein bisschen besser:


  3. Verfolgt man beim Austausch-Sortieren den Weg einer bestimmten Zahl, dann fällt auf, dass manche Zahl von einer "schon ganz guten" Position wieder weit weg transportiert wird. Dieser Nachteil des Sortieralgorithmus kann gemildert werden, wenn man die innere der beiden Schleifen in Richtung fallender Indizes durchlaufen lässt, also zuerst mit dem letzten Element vergleicht, dann mit dem vorletzten usw. Diese Sortiermethode ist unter dem Namen "Bubble-Sort" bekannt.

    Erweitern Sie Ihr Programm um eine entsprechende zweite Sortiermethode! Lassen Sie auch hier die Anzahl der Vergleiche und der Tauschvorgaänge während eines Sortierlaufs mitzählen.


  4. Bewusst Auswählen hilft noch mehr:

    Man kann beim Sortieren durch Austausch viele Tauschoperationen einsparen, indem man erst dann tauscht, wenn man das kleinste der Elemente des Arrays gefunden hat. Die Grundidee dieses "Auswahl-Sort" genannten Algorithmus ist:

      Man suche das kleinste Element des ganzen Arrays und setze es (falls noch nötig) an den Anfang.
      Dann behandle man das Teilarray, das man erhält, wenn man das erste (kleinste!) Element wegläßt, auf eben dieselbe Weise.

    Beachten Sie, dass also der erste Tauschvorgang erst durchgeführt wird, nachdem das kleinste Element des (Teil-)Arrays gefunden wurde!

    Implementieren Sie in Ihrem Sortier-Programm einen solchen "Auswahl-Sort" als dritte Sortiermethode. Und lassen Sie auch hier wieder die Anzahl der Vergleiche und der Tauschvorgänge während eines Sortierlaufs mitzählen.

    [Gesamt-Lösungsvorschlag für die Aufgaben 1 bis 3] [Delphi] [Java]





3.2 Das Effizienz-Problem

Wir haben nun 3 verschiedene Sortier-Methoden kennengelernt; nun wollen wir sie im Hinblick auf ihre Leistungsfähigkeit vergleichen. Weil in der Praxis häufig große Datenmengen zu sortieren sind, interessiert uns besonders, wie sich die Laufzeit der Algorithmen vergrößert, wenn die Anzahl n der zu sortierenden Objekte anwächst. Als Maß für die Laufzeit können wir die Anzahl der während der Sortierung vorzunehmenden Vergleiche und die Anzahl der Tauschvorgänge heranziehen. Man könnte diese Fragen auch entscheiden, indem man die Algorithmen einer wahrscheinlichkeitstheoretisch fundierten Analyse unterzieht; wir wollen das Problem hier aber lieber experimentell angehen, indem wir unsere drei Algorithmen - wie bei einem sportlichen Wettkampf - gegeneinander antreten lassen:


Aufgabe:

  1. Wettlauf der Algorithmen

    Untersuchen Sie die Leistungsfähigkeit der drei oben implementierten Sortier-Algorithmen, indem Sie eine Serie von Sortieraufgaben erledigen und dabei die Anzahl v der Vergleiche und die Anzahl t der Tauschoperationen protokollieren. Berechnen Sie dann für jeden Algorithmus einen mittleren Wert dieser Anzahlen.
    Untersuchen Sie nun, wie sich diese Kennzahlen verändern, wenn sich die Länge n des zu sortierenden Arrays ändert. Führen Sie dazu die eben beschriebene Versuchsserie nochmals mit Arrays des Doppelten bzw. des Zehnfachen der ursprünglichen Länge durch. Können Sie als Ergebnis Ihrer Untersuchungen für jede dieser Kennzahlen eine Formel v(n) bzw. t(n) angeben, die die Abhängigkeit von der Array-Länge n zeigt?
    (Falls Sie als Ergebnis der ersten drei Aufgaben kein geeignetes eigenes Programm zur Verfügung haben, oder Sie es in der Java-Variante nicht schaffen, die jTable-Komponente neu zu dimensionieren, dann können Sie die Experimente auch mit diesem Programm durchführen...)



Natürlich werden Ihre Ergebnisse im Detail davon abhängen, welche Array-Längen Sie genommen und wie viele Sortierläufe Sie durchgeführt haben. Hier zum Vergleich eine Beispiellösung, für die jeweils 5 Läufe durchgeführt wurden:

Mittelwerte der Vergleiche v und Tauschvorgänge t
in Abhängigkeit von der Arraylänge n,
Formeln für v(n) und t(n)
   v(16)   v(32)   v(160)   t(16)   t(32)   t(160) 
 Austausch-Sort  120 496 12720 49,2 218,6 5481,2
 Bubble-Sort  120 496 12720 21,0 90,0 1951,6
 Auswahl-Sort  120 496 12720 12,4 27,4 154,4

Auffällig ist zunächst, dass sich für die Anzahl der Vergleiche bei allen drei Algorithmen und in allen Testläufen jeweils dieselben Werte einstellen, was die enge Verwandtschaft dieser Algorithmen belegt. Ein kurzer Blick in die Quelltexte zeigt, dass der Vergleich bei jedem Durchlauf des innersten Rumpfes einer doppelten FOR-Schleife durchgeführt wird. Kurzes Nachdenken liefert dann die Formel: v(n) = 0,5*n*(n-1). Also wächst die Anzahl der Vergleiche bei diesen drei Sortieralgorithmen ungefähr proportional zu n².

Bei der Zahl der Tauschoperationen ist die Sachlage komplizierter: hier gibt es kein einheitliches Bild. Schauen wir uns zunächst den einfachen Austausch-Sort an: verdoppelt man die Array-Größe, dann steigt die mittlere Anzahl der Tauschvorgänge auf gut das Vierfache an, bei der zehnfachen Array-Größe gar auf das weit über Hundertfache. Dies lässt auf eine Formel der Gestalt t(n) = k * n² schließen. Ein paar Zeilen Rechnerei liefern ungefähr: t(n) = 0,2 * n² .

Beim Bubble-Sort stellt sich die Sachlage recht ähnlich dar; lediglich die Konstante k hat einen viel kleineren Wert: t(n) = 0,08 * n² .

Beim Auswahl-Sort liegt der Fall aber deutlich anders: hier ist der Zusammenhang zwischen t und n eher linear! Eine kurze Rechnung liefert ungefähr: t(n) = 0,9 * n. Ein Blick in den Quelltext zeigt, dass die Konstante k im Extremfall höchstens den Wert 1 annehmen kann!

Diese Ergebnisse weisen den Algorithmus Auswahl-Sort als klaren Sieger aus: bei Gleichstand in der Anzahl der Vergleiche wächst bei ihm die Anzahl der Tauschaktionen höchstens linear in n, wobei zusätzlich sichergestellt ist, dass stets t(n) ≤ n ist. Man sagt: der Tauschaufwand beim Auswahlsort ist "von der Ordnung n", und schreibt dafür kurz: O(n). Der Vergleichsaufwand hingegen ist bei allen drei Algorithmen von der Ordnung n², also kurz: O(n²).



3.3 Quicksort

Gibt es keinen Sortieralgorithmus, der mit weniger als O(n²) Vergleichen auskommt? Angesichts der praktischen Wichtigkeit effizienter Lösungen für das Sortierproblem wurde in den letzten Jahrzehnten einiges an geistiger Anstrengung in die Entwicklung von Sortieralgorithmen investiert. Das Ergebnis ist eine inzwischen nahezu unübersehbare Fülle von verschiedensten Sortieralgorithmen: für fast alle denkbaren Spezialfälle gibt es einen optimalen Algorithmus. Was es allerdings nicht gibt, ist ein Algorithmus, der alle Sortieraufgaben als schnellster erledigt!

Das Arbeitspferd für den Sortier-Alltag ist der Quick-Sort. Dies ist ein Algorithmus, bei dem sowohl der Vergleichs- als auch der Tauschaufwand im Mittel jeweils von der Ordnung n*log(n) ist. (Der Einfachheit halber nehmen wir für "log" den Logarithmus zur Basis 10.) Da der Logarithmus in einem gewissen Sinne die am langsamsten gegen unendlich wachsende elementare Funktion ist, die es gibt, ist O(n*log(n)) auf jeden Fall viel besser als O(n²) und nur wenig schlechter als O(n). Leider ist der Quicksort-Algorithmus aber nicht ganz einfach zu verstehen.

Die Grundidee ist vom Typ "Devide and conquer" ("Teile und herrsche"): man teilt das Problem in mehrere kleinere Teil-Probleme auf und löst zunächst diese, in der Hoffnung, dass sich dann die Teillösungen zu einer Lösung des ursprünglichen Problems zusammenbauen lassen. Beim Quicksort teilt man das gegebene Array in zwei Teil-Arrays auf und sorgt dann (durch entsprechende Vergleichs- und Tauschoperationen) dafür, dass alle Zahlen im ersten Teil-Array kleiner sind als alle Zahlen im zweiten. Weil nun nur noch zwei kleinere Teil-Arrays zu sortieren sind, ist das Problem damit schon kleiner geworden.

Und wie sortiert man nun diese beiden Teil-Arrays? Natürlich mit Quick-Sort: man teilt jedes der beiden Teil-Arrays wiederum in zwei Teil-Arrays auf, von denen das vordere schließlich die kleineren und das hintere die größeren Elemente enthält... uswusw, bis die Teil-Arrays nur noch die Länge 1 haben und damit dann schon sortiert sind! Weil aber die Implementierung eines solchen Algorithmus im Detail nicht ganz einfach ist, sei hier ein universell verwendbarer Quelltext für den Quick-Sort zur Verfügung gestellt:
Die folgende Delphi-Prozedur läßt sich in jedem Programm einsetzen, das Sortieren soll; es muss dazu nur die zu sortierenden Daten in einem (linearen) Array "data" bereithalten und die boolsche Vergleichs-Funktion "IsLessThan(a, b)" sowie die Tausch-Prozedur "Exchange(var a, b)" mit jeweils passenden Datentypen für a und b zur Verfügung stellen. Bitte beachten Sie, dass der Vergleichs-Funktion Werte-Parameter übergeben werden, während die Tausch-Prozedur unbedingt Variablen-Parameter braucht! (Warum wohl?)
   procedure QuickSort;

     procedure qs(li, re: Integer);
       var i, j, v: Integer;
       begin
       i := li; j := re;
       v := data[(li+re) Div 2];
       Repeat
         While IsLessThan(data[i], v) do
           i := i + 1;
         While IsLessThan(v, data[j]) do
           j := j - 1;
         If i <= j then begin
           Exchange(data[i], data[j]);
           i := i + 1;
           j := j - 1;
           end;
       until i > j;
       If li < j then qs(li, j);
       If i < re then qs(i, re);
       end;

     begin
     qs(0, Pred(max));
     end;
Wenn Sie sich diesen Quelltext genauer anschauen, dann sehen Sie, dass er ganz schön raffiniert ist: die Prozedur "QuickSort" enthält eine lokale Prozedur "qs()", der zwei Integerwerte übergeben werden. Diese beschreiben den Index-Bereich des Arrays "data", in dem der aktuelle Aufruf von "qs" wirksam wird. Das Array "data" muss "max" Felder enthalten, die beginnend mit "0" durchnummeriert werden. Der erste Aufruf von "qs" geschieht in der Prozedur "QuickSort" selbst, und dort wird der gesamte Index-Bereich des "data"-Arrays übergeben.
Die folgende Java-Prozedur läßt sich in jedem Programm einsetzen, das Sortieren soll; es muss dazu nur die zu sortierenden Daten in einem (linearen) Array "data" bereithalten und die boolsche Vergleichs-Funktion "IsLessThan(a, b)" sowie die Tausch-Prozedur "switchData(i, j)" mit jeweils passenden Datentypen für a, b, i und j zur Verfügung stellen. Bitte beachten Sie, dass der Vergleichs-Funktion direkt die zu vergleichenden Werte übergeben werden müssen, hingegen der Tausch-Prozedur die Indizes der zu tauschenden Zahlen im "data"-Array! (Warum wohl?)
   public void QuickSort {
     qs(0, Pred(max));
   }

   private void qs(int li, int re) {
     int i = li;
     int j = re;
     int v = data[(li+re) / 2];
     do {
       while (isLessThan(data[i], v)) {
         i = i + 1;
       }
       while (isLessThan(v, data[j])) {
         j = j - 1;
       }
       if (i <= j) {
         switchData(i, j);
         i = i + 1;
         j = j - 1;
       }
     } while (i ≤ j);
     if (li < j) { qs(li, j) };
     if (i < re) { qs(i, re) };
   }
Wenn Sie sich diesen Quelltext genauer anschauen, dann sehen Sie, dass er ganz schön raffiniert ist: die Prozedur "QuickSort" ruft eine Prozedur "qs()" auf, der zwei Integerwerte übergeben werden. Diese beschreiben den Index-Bereich des Arrays "data", in dem der aktuelle Aufruf von "qs" wirksam wird. Das Array "data" muss "max" Felder enthalten, die beginnend mit "0" durchnummeriert werden. Beim ersten Aufruf von "qs" in der Prozedur "QuickSort" wird der gesamte Index-Bereich des "data"-Arrays übergeben.
Ganz am Ende der Prozedur "qs" wird's dann total abenteuerlich: hier ruft "qs" sich selbst zweimal auf! Wenn das mal keine Endlosschleife ergibt! Aber keine Angst, es ist schon dafür gesorgt, dass solche Katastrophen nicht passieren: jeder innere Aufruf von "qs" geschieht mit Argumenten, die nur eine echte Teilmenge des aktuell bearbeiteten Teil-Arrays umfassen, weshalb der zu bearbeitenden Array-Ausschnitt mit jedem inneren Aufruf kleiner wird. Das ist übrigens die Stelle, wo "Divide and Conquer" implementiert ist: hier wird das aktuelle Teil-Array sortiert, indem es seinerseits in zwei Teile geteilt wird, welche jeder für sich sortiert werden. Für Teil-Arrays, deren Länge 2 Felder unterschreitet (also: li ≥ j oder: i ≥ re), erfolgt schließlich kein weiterer Aufruf von "qs" mehr.

Eine solche Programmierstruktur nennt man "rekursiv". Später werden wir uns noch genauer mit der "Rekursion" beschäftigen, aber im Augenblick wollen wir diese elegante Programmieridee einfach mal benutzen.



Aufgaben:

  1. Das Arbeitspferd des Sortier-Alltags: Quick-Sort

    Implementieren Sie mit Hilfe des oben aufgeführten Quelltextes in Ihrem Sortier-Programm einen Quick-Sort als zusätzliche vierte Sortiermethode.
    Zählen Sie auch hier die Anzahl der Vergleiche und die Anzahl der Tauschaktionen.
    [Lösungsvorschlag] [Delphi] [Java]


  2. Abschluss-Test "Jeder gegen jeden"

    Führen Sie den oben angegebenen Vergleichstest der Sortieralgorithmen nun auch noch für den Quick-Sort durch. (Falls Sie als Ergebnis der vorigen Aufgabe kein geeignetes eigenes Programm zur Verfügung haben, können Sie dieses nehmen.)

    Die für Quick-Sort gültigen Formeln für v(n) und t(n) sind von der Form k*n*log(n). Bestimmen Sie mit Hilfe Ihrer Messergebnisse die jeweils passenden Werte für die Konstante k.


Wir wollen nun den Gesamtaufwand für die einzelnen Sortieralgorithmen abschätzen. Da Vergleichen und Tauschen die beiden einzigen "aufwändigen" Funktionen sind, die beim Sortieren anfallen, erscheint es sinnvoll, aus den Formeln für v(n) und t(n) eine neue Funktion a(n) zu konstruieren, die eben diesen Gesamtaufwand messen soll. Zunächst ist klar, dass a(n) wachsen muss, wenn entweder v(n) oder t(n) wächst. Andererseits haben wir keine weiteren Detailinformationen darüber, wie schwer es einer CPU fällt, zwei Datensätze zu vergleichen oder zu vertauschen. Daher ist der einfache Ansatz a(n) = v(n) + t(n) naheliegend und zumindest nicht offensichtlich unvernünftig. Das Gesamtergebnis unserer Untersuchungen könnte nun also so aussehen (wieder mit meinen eigenen Daten ermittelt):


   Austausch-Sort   Bubble-Sort   Auswahl-Sort   Quick-Sort 
 v(n)  0,5*n² 0,5*n² 0,5*n²   4,3*n*log(n)  
t(n) 0,2*n² 0,08*n² 0,9*n 1,0*n*log(n)
a(n) 0,7*n² 0,58*n²  0,5*n²+0,9*n  5,3*n*log(n)


Zeichnet man die Schaubilder der Funktionen a(n) für die verschiedenen Sortieralgorithmen in ein gemeinsames Koordinatensystem, dann zeigt sich, dass für genügend große n der Quick-Sort eindeutig gewinnt: zwar ist sein Aufwand bei kleinen Array-Größen etwas größer als der der direkten Sortiermethoden, aber bei kleinen Arrays geht das Sortieren ohnehin schnell vonstatten; entscheidend ist, dass das Schaubild der Aufwandsfunktion des Quick-Sort bei großen Arrays von einer Geraden kaum zu unterscheiden ist:




Die Grafik zeigt, dass sich die Mühe kaum gelohnt hat, die wir uns um die Verbesserung der direkten Sortiermethoden gemacht haben: die 3 Parabeln verlaufen in schöner Eintracht gemeinsam nach oben und unterscheiden sich kaum voneinander! Die direkten Sortier-Algorithmen sind alle vom selben "Komplexitätsgrad", nämlich O(n²).

Hingegen ist der Quick-Sort-Algorithmus deutlich der "Beste", zumindest unter den hier untersuchten Varianten. Sein Komplexitätsgrad ist O(n*log(n)). In der Tat ist der Quick-Sort der in der Praxis am häufigsten eingesetzte Sortier-Algorithmus. Genauere Analysen zeigen aber, dass es spezielle Situationen gibt, in denen er fast so schlecht arbeitet wie die direkten Sortiermethoden. Auch wenn solche Situationen im Allgemeinen nur sehr selten eintreten, kann sich eine weitere Optimierung des Quicksort gegen diese Schwächen doch lohnen. Genaueres dazu erfahren Sie in Ihrem kommenden Informatik-Studium ;-)




Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel