Unabhänging von der Wahl des Pivotwertes wird die Menge ungefähr mal geteilt.
Vereinfacht dargestellt müssen in jeder Teilmenge M Operationen durchgeführt werden, wobei
M die Anzahl der Elemente in der Teilmenge selbst bezeichnet. Insgesamt ergibt sich somit
eine Laufzeitkomplexität von
für den QuickSort.
Ein weiterhin sehr interessanter Punkt bei der Laufzeitbetrachtung ist die Analyse des Speicherplatzverbrauchs des Algorithmus.
Wie später noch zu sehen wird, gibt es deutliche Unterschiede. Eine rekursive Implementiung kann im worst case Speicherplatz
der Größe N verbrauchen. Im krassen Gegensatz steht dazu die iterative Implementierung, die gewährleistet, nicht
über eine Größe von zu kommen.
Die folgenden Tabellen mit ihren Daten wurden mit den hier gezeigten Implementierungen erstellt. Alle Tests wurden auf der gleichen Datenmenge erstellt, und gewinnen somit kokrete Aussagekraft im Vergleich der Implementierungen. In allen Tabellen befinden sich Werte für eine rekursive wie auch für eine iterative Variante (siehe Implementierung). Bei beiden Lösungen ist die Anzahl der Vergleichs- und Tauschoperationen gleich. Nur die letzten Spalten sind jeweils Implementierungsbezogen. Die letzte Spalte gibt die maximale Stackgröße beim Ablauf der iterativen Funktion. Dagegen ist in der vorletzten Spalte die maximale Rekursionstiefe der rekursiven Funktion sichtbar. Obwohl diese verschiedenen Werte nicht direkt miteinander vergleichbar sind, geben sie eine ungefähre Auskunft darüber, wieviel Speicherplatz die jeweiligen Implementierungen in Anspruch nehmen. Man beachte, daß bei jedem Rekursionsaufruf die lokalen Variablen gespeichert werden müssen, um späpter restauriert werden zu können. Wenn die Rekursion eine bestimmte Tiefe erreicht, so kann man davon ausgehen, daß der Stack eine Größe der Rekrusionstiefe mal die Größe eines Funktionsframes erreicht. Dies geschieht bei der iterativen Vorgehensweise nicht direkt, da kein Aufruf ihrer selbst erfolgt. Die Iterative Lösung behandelt das Speichern von benötiger Information selbst, durch die explizite Verwendung eines eigenen Stacks, und erzielt somit einen wesentlich geringeren Speicherverbrauch.
Tabelle 1 entstand durch die Sortierung einer Menge mit zufällig generierten Werten. Dies sollte einen average case inszenieren.
Zu beachten ist, daß die Vergleichszahlen stark von den konkreten Werten abhängen und somit von der obigen Formel abweichen.
Elemente | Vergleiche | Tauschoperationen | Rekursionstiefe | Stackgröße |
5 | 20 | 4 | 4 | 4 |
10 | 49 | 9 | 6 | 4 |
100 | 793 | 155 | 11 | 10 |
1000 | 15984 | 2340 | 27 | 12 |
10000 | 614647 | 25157 | 131 | 12 |
In der folgenden Tabelle ist, wie zuvor, eine Auflistung der entsprechenden Werte zu sehen. Die veränderte Eigenschaft ist hier
die Anordnung der Datenelemente in der zu sortierenden Datenemenge. Der Test spiegelt das Verhalten des Quicksort auf einer bereits
vorsortierten Menge, hier aufsteigend vorsortiert. Man beachte, daß der Algorithmus auf solchen Datenmengen unakzeptabel schlechte Laufzeiten entwickelt.
Bei der rekursiven Lösung ist zudem noch zu sehen, wie die Rekusionstiefe linear
mit der Anzahl der Elemente wächst, was sehr viel Speicherplatz in Anspruch nimmt. Dagegen ist die iterative Lösung mit einem
konstant großen Stack speicherplatzsparend.
Elemente | Vergleiche | Tauschoperationen | Rerusionstiefe | Stackgröße |
5 | 22 | 4 | 4 | 4 |
10 | 72 | 9 | 9 | 4 |
100 | 5247 | 99 | 99 | 4 |
1000 | 502497 | 999 | 999 | 4 |
10000 | 50024997 | 9999 | 9999 | 4 |
Bei der nächsten Tabelle waren die Daten bereits absteigend vorsortiert. Es ist zu sehen, daß sich die Werte kaum zu dem vorherigen Fall unterscheiden.
Elemente | Vergleiche | Tauschoperationen | Rerusionstiefe | Stackgröße |
5 | 20 | 4 | 4 | 4 |
10 | 67 | 9 | 9 | 4 |
100 | 5197 | 99 | 99 | 4 |
1000 | 501997 | 999 | 999 | 4 |
10000 | 50019997 | 9999 | 9999 | 4 |
Nächste Seite: Implementierung
Aufwärts: QuickSort
Vorherige Seite: Beispiel
Inhalt