Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/wordpress/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 迭代字符串追加的时间复杂度实际上是O(n^2)还是O(n)?_Python_String_Algorithm_Time Complexity_String Concatenation - Fatal编程技术网

Python 迭代字符串追加的时间复杂度实际上是O(n^2)还是O(n)?

Python 迭代字符串追加的时间复杂度实际上是O(n^2)还是O(n)?,python,string,algorithm,time-complexity,string-concatenation,Python,String,Algorithm,Time Complexity,String Concatenation,我正在解决CTCI的一个问题 第1章的第三个问题是,你需要一个字符串,比如 “约翰·史密斯先生” 并要求您将中间空格替换为%20: 'Mr%20John%20Smith' 作者用Python提供了这个解决方案,称之为O(n): 我的问题: 我知道这是O(n),从左到右扫描实际字符串。但是Python中的字符串不是不可变的吗?如果我有一个字符串,并且我使用+操作符向其中添加了另一个字符串,它是否分配了必要的空间,在原始字符串上复制,然后在附加字符串上复制 如果我有一个长度为1的n字符串集合,则需要

我正在解决CTCI的一个问题

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

“约翰·史密斯先生”

并要求您将中间空格替换为
%20

'Mr%20John%20Smith'

作者用Python提供了这个解决方案,称之为O(n):

我的问题:

我知道这是O(n),从左到右扫描实际字符串。但是Python中的字符串不是不可变的吗?如果我有一个字符串,并且我使用
+
操作符向其中添加了另一个字符串,它是否分配了必要的空间,在原始字符串上复制,然后在附加字符串上复制

如果我有一个长度为1的
n
字符串集合,则需要:

1+2+3+4+5+…+n=n(n+1)/2

或者O(n^2)时间,是吗?或者我在Python处理附加的方式上搞错了


或者,如果你愿意教我如何钓鱼:我将如何为自己找到答案?我试图用谷歌搜索一个官方消息来源,但没有成功。我发现,但这没有任何关于字符串的内容。

在Python的标准实现CPython中,有一个实现细节,使得它通常是O(n),在中实现。如果Python检测到左参数没有其他引用,它将调用
realloc
,试图通过调整字符串大小来避免复制。这不是您应该依赖的东西,因为这是一个实现细节,并且因为如果
realloc
最终需要频繁移动字符串,那么无论如何性能都会降低到O(n^2)


没有奇怪的实现细节,由于涉及到二次复制量,该算法是O(n^2)。这样的代码只在一个具有可变字符串的语言中是有意义的,比如C++,甚至在C++中,你也希望使用<代码> += 。 字符串连接最好用
''完成。join(seq)
是一个
O(n)
过程。相反,使用
'+'
'+='
运算符可能会导致一个
O(n^2)
过程,因为可能会为每个中间步骤生成新字符串。CPython 2.4解释器在某种程度上缓解了这个问题;然而,
'.join(seq)
仍然是最佳实践


作者依赖于一个恰好在这里的优化,但并不明确可靠
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)
,但这没关系,因为它在循环之外。

对于未来的访问者:因为这是一个CTCI问题,这里不需要任何关于学习包的参考,特别是根据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'))

应该有人告诉作者关于
urllib.urlencode
@wim这是一个关于数组和字符串的实践问题。这本书的目的是教授面试问题,这通常要求你重新发明轮子来查看面试者的思维过程。因为它是Python,我认为做一个
rtrim
replace
将是更可取的,并且是大致的
O(n)
。在字符串上复制似乎是最不有效的方法。@r您能解释一下复制是如何花费固定时间的吗?我正在查看您链接的代码。。。这段代码的很大一部分似乎是在清理/删除所附加字符串的指针/引用,对吗?然后在最后执行
\u PyString\u Resize(&v,new\u len)
为连接的字符串分配内存,然后执行
memcpy(PyString\u AS\u string(v)+v\u len,PyString\u AS\u string(w),w\u len)哪个进行复制。如果就地调整大小失败,则会执行
PyString\u Concat(&v,w)(我假设这意味着原始字符串地址末尾的连续内存不可用时)。这是如何显示加速的?我在上一篇评论中已经没有空间了,但我的问题是我是否正确理解了代码,以及如何解释这些代码的内存使用/运行时。@user5622964:哎呀,记错了奇怪的实现细节。没有有效的调整策略;它只是调用
realloc
并希望得到最好的结果工作?根据它有定义
void*memcpy(void*destination,const void*source,size\u t num)和说明:
“将num字节的值从源指向的位置直接复制到目标指向的内存块。”
在本例中,num是附加字符串的大小,而source是第二个字符串的地址,我猜?但是为什么目的地(第一个字符串)+len(第一个字符串)?双内存?@user5622964:这是指针算法。如果你想了解CPython源代码到奇怪的实现细节,你需要知道C。超级压缩版本是
PyString\u AS\u STRING(v)
是第一个字符串数据的地址,添加
v_len
会在字符串的数据结束后立即获得地址。这个答案很好,因为它告诉您如何连接字符串。在计算运行时的上下文中,这个答案是精确的。
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'))