Quicksort wird als "an Ort und Stelle" beschrieben, verwendet jedoch eine Implementierung wie:
def sort(array):
less = []
equal = []
greater = []
if len(array) > 1:
pivot = array[0]
for x in array:
if x < pivot:
less.append(x)
if x == pivot:
equal.append(x)
if x > pivot:
greater.append(x)
return sort(less) + equal + sort(greater)
else:
return array
Sie müssen für jede Rekursion eine Kopie der Liste erstellen. Bei der ersten Rückkehr haben wir im Gedächtnis:
- Array
- größer + gleich + kleiner
Dann haben wir durch die zweite Rekursion über alle Unterlisten:
- Array
- größer, gleich, kleiner von der ersten Rekursion
- größer + gleich + kleiner von kleiner1, größer + gleich + kleiner von größer1
usw...
Ist das nur schlecht geschriebener Code oder habe ich Recht, wenn ich denke, dass Sie für eine große Liste tatsächlich proportional genügend zusätzlichen Speicherplatz benötigen, um diesen zu speichern?
Wenn ich an etwas denke, das "an Ort und Stelle" ist, denke ich an die Blasensortierung, bei der einfach Elemente in der Liste ausgetauscht werden, z. B .: Http://en.wikipedia.org/wiki/File:Bubble-sort-example-300px. gif
BubbleSort benötigt nur 1 zusätzliche Variable, um ein potenziell vertauschtes Element zu speichern.
quelle
Antworten:
Diese spezielle Implementierung von Quicksort ist nicht vorhanden. Die Datenstruktur wird als Liste behandelt, die nur in eine Richtung wachsen kann (in diesem Fall wäre eine Zusammenführungssortierung einfacher und schneller). Es ist jedoch möglich, eine direkte Implementierung von Quicksort zu schreiben, und dies wird normalerweise so dargestellt.
In einer In-Place-Implementierung wird die Sortierfunktion stattdessen nicht rekursiv für neu erstellte Arrays aufgerufen, die immer kleiner werden, sondern für Grenzen, die immer näher kommen.
(Hüten Sie sich vor diesem Code, ich habe ihn nur eingegeben, nicht bewiesen. Alle Fehler, die Sie einzeln beheben können, können Sie beheben. Bei Implementierungen in der Praxis wird für kleine Größen ein anderer Algorithmus verwendet, der jedoch kein Einfluss auf das asymptotische Verhalten hat -world-Implementierungen würden einen besseren Pivot wählen, aber darauf werde ich hier nicht eingehen , da dies für diese Frage nicht wirklich der Fall ist.)
Die Wikipedia-Seite enthält sowohl eine nicht vorhandene als auch eine direkte Version des Algorithmus.
Quicksort wie hier geschrieben erfordertO ( d) + s zusätzlicher Speicher, wo d ist die Tiefe der Rekursion (die von der Pivot-Qualität abhängt) und s ist die Elementgröße. Die Stapelgrößenanforderung kann verbessert werden: Es gibt zwei rekursive Aufrufe, und wir können den zweiten zu einem Endaufruf machen (der keinen Stapel verbraucht). Wenn wir immer zuerst den rekursiven Aufruf für die kleinere Hälfte ausführen, dann die maximale Stapelgröße für Array-Längen bis zun befriedigt S^(n)≤S^(m) mit m≤n/2≤S^(n/2) , damit S^(n)≤lg2(n)S^(1) . So können wir erreichenO(logn)+s zusätzlicher Speicherbedarf, unabhängig von der Wahl des Drehpunkts.
Es gibt viele andere Sortieralgorithmen, die direkt implementiert werden können, einschließlich Einfügesortierung, Auswahlsortierung und Heap-Sortierung. Die einfache Form der Zusammenführungssortierung ist nicht vorhanden, es gibt jedoch eine komplexere Variante .
Vielen Dank an Aryabhata für den Hinweis, dass Quicksort immer in ausgeführt werden kannlg(n) Stapel und dass es eine Variante der Zusammenführungssortierung mit beiden gibt O(1) zusätzlicher Speicher und O(nlog(n)) Laufzeit.
quelle
Zusätzlich zu Gilles 'Antwort können Sie Quicksort auch mit Ihrem Code einrichten, wenn Sie verknüpfte Listen anstelle von Arrays verwenden. Stellen Sie einfach sicher, dass Sie das Element aus der ursprünglichen Liste entfernen, wenn Sie es an eine der kleineren Listen anhängen.
Der folgende Pseudocode setzt / garantiert:
new list
Erstellt eine Liste bestehend aus einem Kopf mit demNIL
nächsten Zeiger.sort
Nimmt einen Zeiger auf einen Kopf und gibt einen Zeiger auf das letzte Element der sortierten Liste zurück. Der als Argument angegebene Zeiger ist auch der Kopf der sortierten Liste..
Obwohl aus dem obigen Code eine gewisse Optimierung möglich ist, hat diese Implementierung einen größeren Speicheraufwand als die Array-basierte Implementierung in Gilles 'Antwort. Darüber hinaus leidet es in der Praxis unter dem Problem, dass verknüpfte Listen normalerweise weniger lokalisiert sind und daher eine größere Anzahl von Cache-Fehlern verursachen als das Verwalten derselben Daten in einem Array.
Diese Implementierung ist jedoch von Vorteil, wenn Sie durch Sortieren Zeiger auf die Elemente pflegen müssen. Es ist auch gut, sich dessen bewusst zu sein, wenn Sie Ihre Daten aus Gründen, die nicht mit dem Sortieren zusammenhängen, in einer verknüpften Liste speichern. (Wenn die richtige Sortierung von Bedeutung ist, kommt eine Konvertierung zwischen Liste und Array wahrscheinlich nicht in Frage.)
quelle