我正在解决CTCI的问题。
第1章的第三个问题是你带一个字符串,如
'Mr John Smith '
并要求您用%20
替换中间位:
'Mr%20John%20Smith'
作者在Python中提供了这个解决方案,称之为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
我的问题:
据我所知,就从左到右扫描实际字符串而言,这是O(n)。但是Python中的字符串是不可变的吗?如果我有一个字符串,并使用+
运算符向其添加另一个字符串,它是否分配必要的空格,复制原始字符串,然后复制附加字符串?
如果我有一个长度为1的n
个字符串集合,则需要:
1 + 2 + 3 + 4 + 5 + ... + n = n(n+1)/2
或O(n ^ 2)时间,是吗?或者我错误地认为Python如何处理追加?
或者,如果您愿意教我如何钓鱼:我将如何为自己找到这个?我尝试将Google作为官方来源的尝试失败了。我找到了https://wiki.python.org/moin/TimeComplexity,但这对字符串没有任何意义。
答案 0 :(得分:75)
在CPython中,Python的标准实现,有一个实现细节,通常是O(n),在the code the bytecode evaluation loop calls for +
or +=
with two string operands中实现。如果Python检测到左参数没有其他引用,则会调用realloc
以尝试通过调整字符串的大小来避免副本。这不是你应该依赖的东西,因为它是一个实现细节,因为如果realloc
最终需要经常移动字符串,那么性能会降低到O(n ^ 2)。
没有奇怪的实现细节,由于涉及的二次复制量,算法为O(n ^ 2)。像这样的代码只有在具有可变字符串的语言中才有意义,比如C ++,甚至在C ++中你也想使用+=
。
答案 1 :(得分:34)
作者依赖于恰好在这里进行的优化,但不是明确可靠的。 strA = strB + strC
通常为O(n)
,正在生成O(n^2)
函数。但是,很容易确保整个过程是O(n)
,使用数组:
output = []
# ... loop thing
output.append('%20')
# ...
output.append(char)
# ...
return ''.join(output)
简而言之,append
操作摊销 O(1)
,(尽管您可以通过将数组预先分配到右侧来强化O(1)
大小),制作循环O(n)
。
然后join
也是O(n)
,但这没关系,因为它在循环之外。
答案 2 :(得分:24)
我在Python Speed > Use the best algorithms and fastest tools上找到了这段文字:
字符串连接最好使用
''.join(seq)
O(n)
进程进行。相反,使用'+'
或'+='
运算符可能会导致O(n^2)
进程,因为可能会为每个中间步骤构建新字符串。 CPython 2.4解释器在某种程度上缓解了这个问题;但是,''.join(seq)
仍然是最佳做法
答案 3 :(得分:1)
对于未来的访问者:由于这是一个CTCI问题,此处不需要任何对学习urllib包的引用,特别是根据OP和本书,这个问题是关于数组和字符串。
这是一个更完整的解决方案,灵感来自@ njzk2的伪:
text = 'Mr John Smith'#13
special_str = '%20'
def URLify(text, text_len, special_str):
url = []
for i in range(text_len): # O(n)
if text[i] == ' ': # n-s
url.append(special_str) # append() is O(1)
else:
url.append(text[i]) # O(1)
print(url)
return ''.join(url) #O(n)
print(URLify(text, 13, '%20'))