这个时间复杂度实际上是O(n ^ 2)吗?

时间:2015-11-30 21:06:28

标签: python string algorithm string-concatenation

我正在解决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,但这对字符串没有任何意义。

4 个答案:

答案 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'))