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

Is this time complexity actually O(n^2)?

我正在研究CTCI的一个问题。

第1章的第三个问题是,你用一个字符串,比如

'Mr John Smith '

并要求您用%20替换中间空间:

埃多克斯1〔2〕

作者在python中提供了这个解决方案,称之为o(n):

1
2
3
4
5
6
7
8
9
10
11
12
13
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〔5〕

或者O(n^2)时间,是吗?或者我在python如何处理附加的问题上弄错了?

或者,如果你愿意教我如何钓鱼:我该如何为自己找到这个?我在搜索官方信息源的尝试中失败了。我找到了https://wiki.python.org/moin/timecomplexity,但这对字符串没有任何影响。


在python的标准实现cpython中,有一个实现细节使得这个实现通常是O(N),在代码中实现,字节码评估循环使用两个字符串操作数来调用++=。如果python检测到左边的参数没有其他引用,它会调用realloc来尝试通过调整字符串的大小来避免复制。这不是您应该依赖的东西,因为它是一个实现细节,而且如果realloc最终需要频繁地移动字符串,那么性能无论如何都会降低到O(n^2)。

如果没有奇怪的实现细节,由于所涉及的二次复制量,该算法是O(n^2)。这样的代码只有在具有可变字符串的语言(如C++)中才有意义,甚至在C++中,您也希望使用EDCOX1(1)。


作者依赖于一个恰巧在这里的优化,但并不明确可靠。strA = strB + strC通常是O(n),使函数O(n^2)起作用。但是,很容易确保整个过程是O(n),使用一个数组:

1
2
3
4
5
6
7
output = []
    # ... loop thing
    output.append('%20')
    # ...
    output.append(char)
# ...
return ''.join(output)

简言之,append操作是在O(1)中进行摊销的(尽管您可以通过将数组预先分配到正确的大小来增强O(1),使循环O(n)

然后,join也是O(n),但这没关系,因为它在循环之外。


我在python speed>上找到了这段文本,使用了最好的算法和最快的工具:

String concatenation is best done with ''.join(seq) which is an O(n) process. In contrast, using the '+' or '+=' operators can result in an O(n^2) process because new strings may be built for each intermediate step. The CPython 2.4 interpreter mitigates this issue somewhat; however, ''.join(seq) remains the best practice


对于未来的访问者:因为这是一个CTCI问题,所以这里不需要任何关于学习URLLIB包的参考,特别是根据OP和书,这个问题是关于数组和字符串的。

以下是一个更完整的解决方案,灵感来自@njzk2的伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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'))