Ich arbeite an einem Problem mit CTCI.
Das dritte Problem in Kapitel 1 besteht darin, dass Sie eine Zeichenfolge wie z
'Mr John Smith '
und fordert Sie auf, die Zwischenräume durch Folgendes zu ersetzen %20
:
'Mr%20John%20Smith'
Der Autor bietet diese Lösung in Python an und nennt sie O (n):
def urlify(string, length):
'''function replaces single spaces with %20 and removes trailing spaces'''
counter = 0
output = ''
for char in string:
counter += 1
if counter > length:
return output
elif char == ' ':
output = output + '%20'
elif char != ' ':
output = output + char
return output
Meine Frage:
Ich verstehe, dass dies O (n) ist, wenn die tatsächliche Zeichenfolge von links nach rechts durchsucht wird. Aber sind Strings in Python nicht unveränderlich? Wenn ich eine Zeichenfolge habe und mit der eine weitere Zeichenfolge hinzufüge+
Operator , weist sie dann nicht den erforderlichen Speicherplatz zu, kopiert sie über das Original und kopiert sie dann über die anhängende Zeichenfolge?
Wenn ich eine Sammlung von n
Zeichenfolgen mit der Länge 1 habe, dann dauert das:
1 + 2 + 3 + 4 + 5 + ... + n = n(n+1)/2
oder O (n ^ 2) Zeit , ja? Oder irre ich mich darin, wie Python mit dem Anhängen umgeht?
Alternativ, wenn Sie mir das Fischen beibringen möchten: Wie würde ich das selbst herausfinden? Ich habe bei meinen Versuchen, eine offizielle Quelle zu googeln, keinen Erfolg gehabt. Ich habe https://wiki.python.org/moin/TimeComplexity gefunden, aber dies hat nichts mit Zeichenfolgen zu tun.
quelle
urllib.urlencode
rtrim
undreplace
wäre bevorzugter und im Baseballstadion vonO(n)
. Das Kopieren über Zeichenfolgen scheint der am wenigsten effiziente Weg zu sein.Antworten:
In CPython, der Standardimplementierung von Python, gibt es ein Implementierungsdetail, das dies normalerweise zu O (n) macht, das in dem Code
+
+=
implementiert ist, den die Bytecode-Auswertungsschleife für oder mit zwei Zeichenfolgenoperanden benötigt . Wenn Python feststellt, dass das linke Argument keine anderen Referenzen enthält, wirdrealloc
versucht, eine Kopie zu vermeiden, indem die Größe der Zeichenfolge geändert wird. Darauf sollten Sie sich niemals verlassen, da es sich um ein Implementierungsdetail handelt undrealloc
die Leistung ohnehin auf O (n ^ 2) abnimmt, wenn die Zeichenfolge häufig verschoben werden muss.Ohne das seltsame Implementierungsdetail ist der Algorithmus aufgrund des quadratischen Kopieraufwands O (n ^ 2). Code wie dieser wäre nur in einer Sprache mit veränderlichen Zeichenfolgen wie C ++ und sogar in C ++, die Sie verwenden möchten, sinnvoll
+=
.quelle
_PyString_Resize(&v, new_len)
wird dann der Speicher für die verkettete Zeichenfolge zugewiesen, und dannmemcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);
wird die Kopie erstellt. Wenn die direkte Größenänderung fehlschlägt, ist dies der FallPyString_Concat(&v, w);
(ich gehe davon aus, dass dies bedeutet, wenn der zusammenhängende Speicher am Ende der ursprünglichen Zeichenfolgenadresse nicht frei ist). Wie zeigt dies die Beschleunigung?realloc
und hofft auf das Beste.memcpy(PyString_AS_STRING(v) + v_len, PyString_AS_STRING(w), w_len);
? Laut cplusplus.com/reference/cstring/memcpy hat es Definitionvoid * memcpy ( void * destination, const void * source, size_t num );
und Beschreibung:"Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination."
Die Zahl ist in diesem Fall die Größe der anhängenden Zeichenfolge, und die Quelle ist die Adresse der zweiten Zeichenfolge, nehme ich an? Aber warum ist dann das Ziel (erste Zeichenfolge) + len (erste Zeichenfolge)? Doppelter Speicher?PyString_AS_STRING(v)
ist die Adresse der Daten der ersten Zeichenfolge. Durch Hinzufügen erhaltenv_len
Sie die Adresse direkt nach der der Zeichenfolge Daten enden.Der Autor verlässt sich auf eine Optimierung, die zufällig hier ist, aber nicht explizit zuverlässig ist.
strA = strB + strC
ist in der RegelO(n)
die Funktion zu machenO(n^2)
. Es ist jedoch ziemlich einfach sicherzustellen, dass der gesamte Prozess ausgeführt wirdO(n)
. Verwenden Sie ein Array:Kurz gesagt, die
append
Operation wird amortisiertO(1)
(obwohl Sie sie stark machen können,O(1)
indem Sie das Array der richtigen Größe vorab zuweisen), wodurch die Schleife entstehtO(n)
.Und dann
join
ist das auchO(n)
, aber das ist okay, weil es außerhalb der Schleife liegt.quelle
Ich habe diesen Textausschnitt in Python Speed gefunden.> Verwenden Sie die besten Algorithmen und schnellsten Tools :
quelle
Für zukünftige Besucher: Da es sich um eine CTCI-Frage handelt, ist hier kein Verweis auf das Lernen des Urllib- Pakets erforderlich, insbesondere gemäß OP und dem Buch. Bei dieser Frage handelt es sich um Arrays und Strings.
Hier ist eine vollständigere Lösung, die vom Pseudo von @ njzk2 inspiriert ist:
quelle