Es gibt einen kleinen Fehler in der Ghoseb- Lösung, der sie zu O (n ** 2) und nicht zu O (n) macht.
Das Problem ist, dass dies funktioniert:
item = l1.pop(0)
Bei verknüpften Listen oder Deques wäre dies eine O (1) -Operation, würde also die Komplexität nicht beeinträchtigen. Da Python-Listen jedoch als Vektoren implementiert sind, kopiert dies den Rest der Elemente von l1 um ein Leerzeichen, eine O (n) -Operation . Da dies bei jedem Durchlauf durch die Liste erfolgt, wird ein O (n) -Algorithmus in einen O (n ** 2) -Algorithmus umgewandelt. Dies kann mithilfe einer Methode korrigiert werden, die die Quelllisten nicht ändert, sondern nur die aktuelle Position verfolgt.
Ich habe versucht, einen korrigierten Algorithmus mit einem einfachen sortierten (l1 + l2) zu vergleichen, wie von dbr vorgeschlagen
def merge(l1,l2):
if not l1: return list(l2)
if not l2: return list(l1)
if l1[-1] > l2[-1]:
l1,l2 = l2,l1
it = iter(l2)
y = it.next()
result = []
for x in l1:
while y < x:
result.append(y)
y = it.next()
result.append(x)
result.append(y)
result.extend(it)
return result
Ich habe diese mit Listen getestet, die mit generiert wurden
l1 = sorted([random.random() for i in range(NITEMS)])
l2 = sorted([random.random() for i in range(NITEMS)])
Für verschiedene Listengrößen erhalte ich die folgenden Timings (100-mal wiederholen):
merge : 0.079 0.798 9.763 109.044
sort : 0.020 0.217 5.948 106.882
Tatsächlich sieht es so aus, als ob dbr richtig ist. Die Verwendung von sorted () ist vorzuziehen, es sei denn, Sie erwarten sehr große Listen, obwohl die algorithmische Komplexität schlechter ist. Die Gewinnschwelle liegt bei rund einer Million Artikeln in jeder Quellenliste (insgesamt 2 Millionen).
Ein Vorteil des Zusammenführungsansatzes besteht jedoch darin, dass das Umschreiben als Generator trivial ist, wodurch wesentlich weniger Speicher benötigt wird (keine Zwischenliste erforderlich).
[Bearbeiten]
Ich habe dies mit einer Situation wiederholt, die näher an der Frage liegt - unter Verwendung einer Liste von Objekten, die ein Feld " date
" enthalten, das ein Datum / Uhrzeit-Objekt ist. Der obige Algorithmus wurde geändert, um ihn .date
stattdessen zu vergleichen , und die Sortiermethode wurde geändert in:
return sorted(l1 + l2, key=operator.attrgetter('date'))
Das ändert die Dinge ein bisschen. Da der Vergleich teurer ist, wird die Anzahl, die wir durchführen, im Verhältnis zur zeitkonstanten Geschwindigkeit der Implementierung wichtiger. Dies bedeutet, dass das Zusammenführen verlorenen Boden ausmacht und stattdessen die sort () -Methode bei 100.000 Elementen übertrifft. Ein Vergleich anhand eines noch komplexeren Objekts (z. B. große Zeichenfolgen oder Listen) würde dieses Gleichgewicht wahrscheinlich noch mehr verschieben.
merge : 0.161 2.034 23.370 253.68
sort : 0.111 1.523 25.223 313.20
[1]: Hinweis: Ich habe tatsächlich nur 10 Wiederholungen für 1.000.000 Elemente durchgeführt und entsprechend skaliert, da es ziemlich langsam war.