Startseite < Informatik < Algorithmen < Sortieren < BucketSort RadixSort / [ DRUCK , 2004 , 2005 , 2006 , 2007 , 2008 , 2009 ] QuickSort < Zufallsvariante 3-Median-Variante / IntroSort > MergeSort HeapSort / BubbleSort SelectionSort InsertionSort / SlowSort BogoSort > Komprimieren Suchen / Primzahltest > Datenstrukturen / Software-Engineering / Programmiersprachen / Künstliche Intelligenz > Schach Privates / Inhalt >
QuickSort
Einer der schnellsten Sortieralgorithmen, welcher auf Vergleichen beruht, ist der QuickSort!
Einführung Theorie: QuickSort
Definition
Beim QuickSort legt man die ganze Arbeit in die Zerlegung. Man wählt ein beliebiges Element (z. B. das erste) und teilt die Liste in drei Teile: die kleineren Elemente, die gleichen Elemente, die größeren Elemente. Wenn diese drei Teillisten selbst sortiert worden sind, kann man sie einfach zum Ergebnis konkatenieren. Die Zerlegung geschieht mithilfe des Filter-Funktionals. [FUNPROG]

QuickSort gehört zur Klasse der Divide-and-Conquer-Algorithmen. [INFO2002]
Komplexität
Wenn die Zerlegung jedes Mal so klappt, dass die Teillisten der kleinen und der großen Elemente ungefähr gleich lang sind, hat dieser Algorithmus einen Aufwand in der Größenordnung O(N log N). Das ist aber nur der durchschnittliche Aufwand. Im worst case, also wenn die Zerlegung jedes Mal sehr unausgewogen erfolgt, ist der Aufwand quadratisch.

In der Praxis ist der QuickSort der effizienteste aller bekannten Sortieralgorithmen. [FUNPROG]
Beispiel
Stellen Sie sich einfach mal vor, Sie hätten in einem Europa-Gewinnspiel einen großen Haufen von Euro- und Centmünzen gewonnen. Sie möchten selbstverständlich wissen, wie viel dieser Münzhaufen wert ist, und beginnen zunächst, den gesamten Münzhaufen in mehrere Haufen aufzuteilen — die 1-Cent-Münzen ganz links, die 2-Cent-Münzen etwas rechts davon, die 5-Cent-Münzen auf einen weiteren Haufen rechts davon usw., um eben später jeden einzelnen Haufen einfacher abrechnen zu können.
Der QuickSort sortiert und Sie rechnen dann ab!
MoneyQuickSort('aktuelle Haufen'):
  1. QS stellt sich auf den 'aktuellen Haufen'.
  2. QS nimmt davon eine beliebige Münze
     und gibt dieser Münze den Namen Pivot.
  3. QS vergleicht nun jede Münze mit dem Pivot.
       a) Ist eine Münze kleiner als Pivot,
          dann wirft QS diese Münze links auf einen neuen Haufen.
       b) Ist eine Münze größer als Pivot,
          dann wirft QS diese Münze rechts auf einen neuen Haufen.
       c) Ist eine Münze gleich dem Pivot,
          dann wirft QS diese Münze vorne auf einen neuen Haufen.
     Bis der 'aktuelle Haufen' weg ist,
     bis er also in maximal drei kleinere Haufen aufgeteilt ist,
     einer vorne, einer links und einer rechts (Dreiecksformation).
  4. Schließlich wirft QS das Pivot noch auf den vorderen Haufen.
  { SELBSTÄHNLICHES VORGEHEN BEI SELBSTÄHNLICHEN PROBLEMEN! (5.-6.) }
  5. Wenn es links vom (bereits leer geräumten) 'aktuellen Haufen'
     einen neuen Haufen gibt, dann wiederholt QS die selbe Prozedur
     von 1. bis 6. mit dem kleineren linken Haufen,
     also MoneyQuickSort('linker Haufen').
     { VORGEHEN WIEDERHOLT SICH MIT LINKEM HAUFEN. }
           5.1. QS stellt sich auf den 'linken Haufen'.
           5.2. QS nimmt davon eine beliebige Münze
                und gibt ihr den Namen Pivot.
           5.3. QS vergleicht nun jede Münze mit dem Pivot usw. ...
           5.4. ...
           5.5. ...
           5.6. ...
     { ABER NICHT VERGESSEN AUCH NOCH RECHTS NACHZUSEHEN! (6.) }
  6. Wenn es rechts vom (bereits leer geräumten) 'aktuellen Haufen'
     einen neuen Haufen gibt, dann wiederholt QS die selbe Prozedur
     von 1. bis 6. mit dem kleineren rechten Haufen,
     also MoneyQuickSort('rechter Haufen').
     { VORGEHEN WIEDERHOLT SICH MIT RECHTEM HAUFEN. }
           6.1. QS stellt sich auf den 'rechten Haufen'.
           6.2. QS nimmt davon eine beliebige Münze
                und gibt ihr den Namen Pivot.
           6.3. QS vergleicht nun jede Münze mit dem Pivot usw. ...
           6.4. ...
           6.5. ...
           6.6. ...

{ SORTIERUNG ABGESCHLOSSEN, wobei hier die Haufen
  im Idealfall dreieckig (best case: O(n * log n)),
  im Normalfall baumartig (average case: O(n * log n))
  und im schlimmsten Fall kettenförmig (worst case: O(n * n))
  angeordnet wären. }
Konkrete Zahlenbeispiele
Der Einfachheit halber sei stets das linke Element das Pivotelement.
Aus Effizienzgründen soll ein glatter (siehe unten oder MoneyQuickSort) QuickSort vorliegen.
Notation: Aus [pivot, ...] wird [kleinere]#pivots#[größere]
  • Sei nun [5,8,4,7,8,2,1,9,2,8,3,6,8,9] die zu sortierende Liste.
    • [5,8,4,7,8,2,1,9,2,8,3,6,8,9]
    • [4,2,1,2,3]#5#[8,7,8,9,8,6,8,9]
    • [2,1,2,3]#4#[]#5#[7,6]#8,8,8,8#[9,9]
    • [1]#2,2#[3]#4#[]#5#[6]#7#[]#8,8,8,8#[]#9,9#[]
  • Sei nun [1,2,3,3,3,4,5,6,7,8,9] die zu sortierende Liste.
    • [1,2,3,3,3,4,5,6,7,8,9]
    • []#1#[2,3,3,3,4,5,6,7,8,9]
    • []#1#[]#2#[3,3,3,4,5,6,7,8,9]
    • []#1#[]#2#[]#3,3,3#[4,5,6,7,8,9]
    • []#1#[]#2#[]#3,3,3#[]#4#[5,6,7,8,9]
    • []#1#[]#2#[]#3,3,3#[]#4#[]#5#[6,7,8,9]
    • []#1#[]#2#[]#3,3,3#[]#4#[]#5#[]#6#[7,8,9]
    • []#1#[]#2#[]#3,3,3#[]#4#[]#5#[]#6#[]#7#[8,9]
    • []#1#[]#2#[]#3,3,3#[]#4#[]#5#[]#6#[]#7#[]#8#[9]
    Wenn bei einer vorsortierten Liste stets das linke Element als Pivotelement ausgezeichnet wird, dann kommt es zum schlechtesten Laufzeitverhalten — es handelt sich hierbei um den sogenannten worst case.
  • Sei nun [9,1,8,2,3,7,3,4,5,6,3] die zu sortierende Liste.
    • [9,1,8,2,3,7,3,4,5,6,3]
    • [1,8,2,3,7,3,4,5,6,3]#9#[]
    • []#1#[8,2,3,7,3,4,5,6,3]#9#[]
    • []#1#[2,3,7,3,4,5,6,3]#8#[]#9#[]
    • []#1#[]#2#[3,7,3,4,5,6,3]#8#[]#9#[]
    • []#1#[]#2#[]#3,3,3#[7,4,5,6]#8#[]#9#[]
    • []#1#[]#2#[]#3,3,3#[4,5,6]#7#[]#8#[]#9#[]
    • []#1#[]#2#[]#3,3,3#[]#4#[5,6]#7#[]#8#[]#9#[]
    • []#1#[]#2#[]#3,3,3#[]#4#[]#5#[6]#7#[]#8#[]#9#[]
    Alle Pivotelemente sind hier unglücklicherweise Extrema der jeweiligen Teilliste, was ebenfalls zum schlechtesten Laufzeitverhalten führt.

    Bei unsortierten Listen kann der schlechteste Fall nicht ausgeschlossen werden.
Implementierung Implementierung des QuickSort
Folgender SML-Code implementiert den QuickSort streng nach Definition — der QuickSort in Reinkultur! Zudem besitzt speziell dieser SML-QuickSort folgende Eigenschaften:
  • Glattheit
    Beispielsweise werden Listen mit gleichwertigen Elementen in O(n) Schritten sortiert.
    Die Rekursion käme praktisch nicht zustande, weil schon im ersten (Rekursions-) Aufruf alle Elemente der zu sortierenden Liste in die mittlere Liste medium aufgenommen werden würden, die beiden anderen Listen small und large hingegen leer blieben.
  • Stabilität
    Die Reihenfolge gleichwertiger Elemente wird stets aufrechterhalten (siehe Filter-Funktion).

    Dabei ist aber der QuickSort als nicht stabiler Sortieralgorithmus bekannt. Man spricht allerdings indirekt von der „In-Placement-Version” des QuickSort. Diese SML-Implementierung zeigt einen stabilen QuickSort!
Mehr zu diesen Eigenschaften finden Sie unter Sortieren.


Aus dieser Implementierung geht deutlich hervor, dass der QuickSort ein Divide-and-Conquer - Algorithmus ist und mit einer kaskadenartigen bzw. baumartigen Rekursion realisiert werden muss. Diese Rekursion erinnert stark an eine Inorder-Traversion eines binären Baumes. Deshalb spricht man auch häufig davon, dass der QuickSort eine Baumstruktur aufbaut. Der einzelne Knoten des Binärbaumes wäre demnach 3-teilig:
  1. Links (hier: quicksort small)
    Der Verweis auf einen weiteren Knoten des Baumes bzw. auf die sortierte Teilliste aller kleineren Elemente bzgl. des Pivotelements.
  2. Mitte (hier: medium)
    Die Liste aller Elemente, die dem Pivotelement gleichwertig sind, inklusive des Pivotelements selbst.
    Dieser Mitte verdankt man übrigens ein deutlich besseres Laufzeitverhalten, wenn Listen mit verhältnismäßig vielen gleichwertigen Elementen sortiert werden (Glattheit).
  3. Rechts (hier: quicksort large)
    Der Verweis auf einen weiteren Knoten des Baumes bzw. auf die sortierte Teilliste aller größeren Elemente bzgl. des Pivotelements.
Implementierung mit SML
(* filter: Hilfsfunktion
   gibt nur die Elemente zurück,
   die den Vergleich mittels elem und cmp bestehen.
   Die Reihenfolge dieser Elemente bleibt erhalten bzw. stabil! *)


fun filter nil elem cmp = nil
  | filter (x::xl) elem cmp  =
      if (cmp(x, elem))
        then x :: filter xl elem cmp
        else filter xl elem cmp;

(* quicksort: klar!
   small: Elemente, die kleiner sind als das Pivotelement.
   medium: Pivotelement mit den Elementen, die dem Pivotelement gleichen.
   large: Elemente, die größer sind als das Pivotelement. *)


fun quicksort nil = nil
  | quicksort (pivot::xl) =
      let
        val small = filter xl pivot (op <);
        val medium = pivot :: filter xl pivot (op =);
        val large = filter xl pivot (op >);
      in
        (quicksort small) @ medium @ (quicksort large)
      end;

(* Anwendungsbeispiel *)

val output1 = quicksort [3, 6, 2, 9, 1, 8, 7, 4, 5];
val output2 = quicksort [7, 7, 7, 2, 7, 2, 2, 2];
val output3 = quicksort [~3, 3, ~2, 2];

(* Ausgabe:
   val output1 = [1,2,3,4,5,6,7,8,9] : int list
   val output2 = [2,2,2,2,7,7,7,7] : int list
   val output3 = [~3,~2,2,3] : int list *)
Die Prolog-Implementierung des QuickSort ist der vorhergehenden SML-Implementierung sehr ähnlich, nur dass dieser Algorithmus die Glattheit nicht berücksichtigt, weil hier die zu sortierende Liste nur in zwei kleinere Listen aufgeteilt wird. Die mittlere Liste gibt es in diesem Beispiel nicht, sie besteht praktisch nur aus dem Pivotelement selbst.
Implementierung mit Prolog
% ?-quicksort([5, 2, 7, 4, 9, 6, 2, 1, 5, 3, 8], SL).

quicksort([], []).

quicksort([Pivot|L], SL) :-
    divide(Pivot, L, L_Lower, L_Larger),
    quicksort(L_Lower, SL_Lower),
    quicksort(L_Larger, SL_Larger),
    append(SL_Lower, [Pivot|SL_Larger], SL).

% divide: Liste wird in zwei Listen [ < X ] und [ >= X ] zerlegt.

divide(_, [], [], []).

divide(X, [Y|L], [Y|L_Lower], L_Larger) :-
    X > Y,
    !,
    divide(X, L, L_Lower, L_Larger).

divide(X, [Y|L], L_Lower, [Y|L_Larger]) :-
    divide(X, L, L_Lower, L_Larger).
Der Schreibaufwand mit Haskell ist deutlich kleiner als bei der entsprechenden Prolog-Implementierung. Auch hier wird die Glattheit nicht berücksichtigt. Die Elemente, die dem Pivotelement p gleichen, werden mit den Elementen vermengt, welche größer (g) als das Pivotelement sind.

Möglicherweise gibt es keine andere Sprache, in der der QuickSort noch kürzer implementiert werden kann.
Implementierung mit Haskell
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (p:r) = quicksort[k|k<-r,k<p] ++ [p] ++ quicksort[g|g<-r,g>=p]
In-Placement-QuickSort
Im Folgenden wird der QuickSort als In-Placement-Sortierverfahren implementiert, was einen höheren und komplizierten Programmieraufwand zur Folge hat. Um die Ordnung einer Teilliste bzgl. des Pivotelements herzustellen, benötigt man lediglich so viel Speicherplatz, wie die kaskadenartige Rekursion an Stack benötigt. Um in-place sortieren zu können, geht man meist zwei Kompromisse ein:
  • Verlust der Glattheit!
    Man teilt die Liste bei jedem Rekursionsschritt nur noch in zwei Teile: Die Elemente, die dem Pivotelement gleichwertig sind, werden je nach Art der Implementierung in die linke bzw. in die rechte Teilliste geswappt; die mittlere Liste besteht praktisch nur noch aus einem einzigen Element, dem Pivotelement selbst.
    Aus diesem Grunde werden selbst die Elemente, die dem Pivotelement gleichwertig sind, nochmals (überflüssigerweise) in den folgenden Rekursionsschritten betrachtet, bis diese selbst zu Pivotelementen werden oder bis der triviale Fall (Terminierung) eintritt.
  • Verlust der Stabilität!
    Elemente, die dem Pivotelement gleichwertig sind, werden genauso wie die anderen Elemente behandelt. Durch das permanente Vertauschen der Elemente innerhalb der zu sortierenden Liste kann die Reihenfolge gleichwertiger Elemente nicht mehr nachvollzogen werden.
Wie ein In-Placement-QuickSort zu einem glatten Sortierverfahren modifiziert werden kann, wird beispielsweise recht ausführlich in [ALGODAT S. 96f] beschrieben.
Jedoch geht die Stabilität beim In-Placement-QuickSort für immer verloren. Bei Wunsch nach Stabilität sollte man auf andere stabile Sortierverfahren zurückgreifen, wie zum Beispiel auf eine stabile Implementierung des HeapSort.
Implementierung mit Delphi
unit UQuickSort;

interface

procedure QuickSort(var A: array of Integer);

implementation

procedure QuickSort(var A: array of Integer);

  procedure QSort(LoIndex, HiIndex: Integer);
  var
    Lo, Hi: Integer;
    Pivot: Integer;
    Swap: Integer;
  begin
    // Wähle stets das mittlere Element als Pivotelement.
    Pivot := A[(LoIndex + HiIndex) div 2];

    // Stelle die Ordnung bzgl. des Pivotelements her.
    Lo := LoIndex;
    Hi := HiIndex;
    repeat
      while A[Lo] < Pivot do Inc(Lo);
      while A[Hi] > Pivot do Dec(Hi);
      if Lo <= Hi then
      begin
        Swap := A[Lo];
        A[Lo] := A[Hi];
        A[Hi] := Swap;
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;

    // Gegebenenfalls linke Teilliste sortieren.
    if LoIndex < Hi then QSort(LoIndex, Hi);

    // Gegebenenfalls rechte Teilliste sortieren.
    if Lo < HiIndex then QSort(Lo, HiIndex);
  end;

begin
  QSort(Low(A), High(A));
end;

end.
Da in der hier gezeigten Delphi-Implementierung des QuickSort stets das mittlere Element einer Teilliste als Pivotelement ausgezeichnet wird, entsteht eine unnötige Geschwindigkeitsbremse, nämlich die Berechnung der Indexposition des Pivots: (LoIndex + HiIndex) div 2. Wählt man stets das mittlere Element als Pivotelement, geht man einer Worst-Case-Laufzeitkomplexität nicht aus dem Wege (siehe oben, das dritte konkrete Zahlenbeispiel), schlimmer noch: Der QuickSort büßt an Effizienz ein, wo doch gerade die Effizienz die Stärke des QuickSort darstellen sollte.

Im folgenden Code-Beispiel in C++ wird stets das am weitesten rechts liegende Randelement als Pivotelement ausgezeichnet — es besteht kein besonderer Anlass mehr, das Pivotelement teuer zu ermitteln.

Dies soll aber nicht bedeuten, dass C++ schneller ist als Delphi, sondern nur, dass das folgende Beispiel einen effizienteren Algorithmus implementiert (algorithmusabhängig, nicht programmiersprachenabhängig).
Implementierung mit C++
#include <stdlib.h>
#include <stdio.h>

void quicksort(int *a, int left, int right) {

  /* Betrachtung des (Teil-)Arrays,
   * das mindestens zwei Elemente umfasst,
   * left und right sind die Grenzen des (Teil-)Arrays. */

  if (left < right) {

    int pivot = a[right]; // das rechte Grenzelement, Pivotelement!
    int l = left;
    int r = right;

    do {
      /* Den linken Index so weit nach rechts rücken (erhöhen),
       * solange das l-Element kleiner ist als das Pivotelement. */

      while (a[l] < pivot) l++;

      /* Den rechten Index so weit nach links rücken (verkleinern),
       * solange das r-Element größer ist als das Pivotelement. */

      while (a[r] > pivot) r--;

      /* Vertausche die aktuellen r- und l-Elemente und
       * erhöhe l und verringere r jeweils um 1,
       * falls l noch links von r (rechts) ist! */

      if (<= r) {
          int swap = a[l];
          a[l] = a[r];
          a[r] = swap;
          l++;
          r--;
      }
    } while (<= r); // bis sich l und r irgendwo in der Mitte treffen.

    quicksort(a, left, r);  // sortiere die linke Teilliste.
    quicksort(a, l, right); // sortiere die rechte Teilliste.
  }
}

// Kleiner Test: Lass quicken!
int main() {

  int values[] = {3, 9, 2, 0, 5, 18, 11, 4, 77, 16};
  int count = sizeof(values) / sizeof(*values);

  // Ausgabe der Werte (vorher)
  for (int i = 0; i < count; i++) printf("%5d", values[i]);
  printf("\n");

  // Sortiere alle Elemente von 0 bis count-1!
  quicksort(values, 0, count - 1);

  // Ausgabe der Werte (nachher)
  for (int i = 0; i < count; i++) printf("%5d", values[i]);
  printf("\n");

  system("PAUSE");
  return 0;
}
Implementierung mit Perl
Dank Christian Weßel kann ich Ihnen auch einen QuickSort in Perl präsentieren, sehen Sie selbst!
#!/usr/bin/perl

sub QuickSort {
  my ($comp,$left,$right,@list) = @_;

  if ($left < $right) {
    $pivot = $list[$right];
    $l = $left;
    $r = $right;

    do {
      while (&$comp($list[$l], $pivot) && $l<$right) {$l++};
      while (&$comp($pivot, $list[$r]) && $r>$left) {$r--};
      if ($l <= $r) {
        ($list[$l], $list[$r]) = ($list[$r], $list[$l]);
        $l++;
        $r--;
      };
    } while ($l <= $r);
    @list = &QuickSort($comp, $left, $r, @list);
    @list = &QuickSort($comp, $l, $right, @list);
  };

  return @list;
};

# --- Anwendungsbeispiel ---

# Vergleichsfunktionen
my $numAsc = sub {return $_[0] < $_[1];};
my $numDesc = sub {return $_[0] > $_[1];};

@A = (4,2,9,3,0,5,1,8,6,7);
@B = &QuickSort($numAsc, 0, $#A, @A);
@C = &QuickSort($numDesc, 0, $#A, @A);

print "# @A (unsortiert)\n";
print "#\n";
print "# @B (aufsteigend; asc)\n";
print "# @C (absteigend; desc)\n";

exit;
Varianten Varianten des QuickSort
Warum eigentlich Varianten?
Selbst ein glatter QuickSort ist in ungünstigen Fällen nicht schnell und kann sogar — wenn auch nur selten — langsamer als ein BubbleSort oder ShakerSort sein.

Bei einem Rekursionsschritt des QuickSort liegt ein ungünstiger Fall vor, wenn das Pivotelement ein Element einer verhältnismäßig kleinen Menge der größten und kleinsten Elemente der jeweiligen Teilliste ist.

Würde man beispielsweise das kleinste Element (Minimum) einer Teilliste als Pivotelement nehmen, so würde im folgenden Rekursionsschritt das Problem „Sortiere Liste der Länge n” nur auf das Problem „Sortiere Liste der Länge n-1” reduziert werden. Fiele nun die Wahl in jedem Rekursionsschritt auf das kleinste Element der jeweiligen Teilliste, dann würde der worst case mit einer Abschätzung von O(n*n) die Folge sein — der Algorithmus degeneriert.
Die Analogie zu SelectionSort wäre somit hergestellt, nur mit dem Unterschied, dass der SelectionSort in n-1 Schritten das Minimum sucht, während der QuickSort nach der Wahl des Pivotelements (hier: zufällig ein Minimum) die übrigen n-1 Elemente mit diesem vergleicht. Die Komplexität wäre in beiden Fällen gleich schlecht (quadratisch).

Der obige SML-QuickSort z. B. wählt stets das linke Grenzelement als Pivotelement. Müsste man also mit diesem SML-QuickSort eine vorsortierte Liste sortieren, hätte man genau den ungünstigsten Fall mit einer Laufzeitabschätzung von O(n*n).
Der worst case kann aber auch bei obiger Delphi-Prozedur, welche immer das mittlere Element der Teilliste als Pivotelement wählt, auftreten. Dabei müssen aber die Elemente (zufälligerweise) dementsprechend ungünstig angeordnet vorliegen (Pivotelement sei stets ein Extremum der jeweiligen Teilliste). Allerdings treten — statistisch gesehen — derartig ungünstige Anordnungen seltener auf als zum Beispiel teilweise vorsortierte Listen.

Mit den QuickSort-Varianten möchte man vorwiegend durch bessere Wahl des Pivotelements das Auftreten der quadratischen Laufzeit O(n*n) weitestgehend ausschließen.
Bekannte Varianten des QuickSort
Es gibt zwei nennenswerte QuickSort-Varianten, deren modifiziertes Verfahren zur Bestimmung des Pivotelements nicht wesentlich die Effizienz des QuickSort mindert:
  1. Zufallsvariante
  2. 3-Median-Variante
Eine andere Variante des QuickSort, welche IntroSort genannt wird und im schlechtesten Falle die Laufzeitabschätzung O(n log n) garantiert, behilft sich im für den QuickSort ungünstigen Falle anderer, jedoch weniger effizienter Sortierverfahren mit einer Laufzeitabschätzung von O(n log n).
Schlimmstenfalls kommt der QuickSort überhaupt nicht zum Zuge, weshalb diese Variante eher nur als Mogelpackung angesehen werden kann.
Ansatz zur Steigerung der Effizienz
Es ist offenbar nicht sehr effizient, die Rekursion bis zum bitteren Ende fortzusetzen. Deshalb kann man sich ernsthaft überlegen, Teillisten der Länge 3 ohne Anwendung von Rekursion zu sortieren (vorzeitige Terminierung). Zudem würde auch der Bedarf an Stackspeicher gemindert werden.
Geschichte Entstehungsgeschichte des QuickSort
QuickSort wurde um 1960 von dem berühmten britischen Informatiker C. A. R. Hoare entwickelt. Damals waren noch keine schnellen Sortieralgorithmen bekannt, und man versuchte daher, die einfachen Sortierverfahren durch raffinierte Programmiertricks zu beschleunigen. Hoare demonstrierte, dass es, wie auch in vielen anderen Fällen, sinnvoller ist, nach besseren Algorithmen zu suchen, als vorhandene Algorithmen durch trickreiche Programmierung zu beschleunigen.

Besonders bemerkenswert ist in diesem Zusammenhang, dass QuickSort ein rekursiver Algorithmus ist, was in den Frühzeiten der Informatik als ineffizient galt. [INFO2002]