假设您循环 n 次,每次迭代都会创建一个空间 n 的字符串,其范围仅在该迭代内(因此在下一次迭代中不再可访问)。我会说我使用 O(n^2) 空间,因为对于 n 次迭代,我使用 n 空间。
但是,从逻辑上讲,如果每个循环都销毁前一个迭代的字符串(n 空间)并用本次迭代的字符串(n 空间)覆盖它,那么在整个循环中,您将只使用 O(n) 空间。我很困惑到底是确认O(n)空间还是O(n^2)空间?
举这个例子:
s = "hello"
for _ in range(len(s)):
newString = s[:]
return newString
您仅测量任一时间所需的最大空间量。 您的循环需要 O(n) 空间,因为正如您所说,任何时候只有一次迭代在范围内。
在没有显式释放内存的垃圾收集语言中,有理由相信您所使用的任何语言的实现,以确保您实际使用的空间量最多与您需要的空间量成正比,因此它不会影响你的空间复杂度。
在大多数情况下,忽略诸如渐近空间复杂性分析的内存碎片之类的问题也是合理的,即使这样的问题可以实际上改变现实世界的渐近复杂性。
您的问题并没有真正的答案,因为动态分配在时间和空间方面都有未知的成本!
算法分析通常假设无限的内存空间可用,以“全局分配”的方式,并且具有恒定的访问成本。该模型没有动态分配的概念。
您可能会发明一种集成动态分配的模型,并具有精心选择的时间和空间成本。如果假设分配“在堆栈上”,则常数时间和 O(n) 空间(在返回时释放内存时仅计算一次)是合理的。对于“堆”分配,你一头雾水。
标题和正文中的问题不相符,所以我在这里回答正文中的问题,因为它相对简单。标题中的问题本身对于 SO 来说太宽泛了,所以我建议您更改它。供参考:“如果您可以想象一本完整的书来回答您的问题,那么您的要求就太多了。”
理论上,最坏情况的空间使用量是 O(n^2),正如您提到的:n n长度字符串的副本。
但实际上,由于垃圾收集(即引用计数将导致不再有任何引用指向它们的字符串从内存中删除)和字符串驻留(即
s[:]
可以返回与 s
相同的对象)。例如在 CPython 3.8.12 中:
>>> s = 'hello'
>>> s is s[:]
True