Python列表切片的空间复杂度

时间:2018-06-23 16:39:03

标签: python string python-3.x list

我很难理解Python列表切片的空间复杂性。

对于

Thread

是为切片分配了新空间(就像在String中那样,因为它们是不可变的)还是在同一数组上完成了操作?

对于类似的东西

arr[2:] = arr[2:][::-1]

ans = [i+1 for i in range(n)]

空间复杂度是多少?它会与ans是字符串(例如for i in range(k): ans[i:] = ans[i:][::-1] 之类的字符串)不同还是相同?

2 个答案:

答案 0 :(得分:1)

简而言之,每个列表切片操作都涉及复制相关对象引用(而不是对象本身)。

在您的示例中:

  • arr[2:]复制从索引2开始存储在arr中的对象引用,并将它们放入未命名的新列表中(我将其称为L1)。
  • [::-1]复制L1中的对象引用,并将它们以相反的顺序放置到未命名的新列表中(我将其称为L2)。 / li>
  • arr[2:] = ...arr中存储的对象引用替换为L2中存储的对象引用。

值得注意的是,这些都不是可以保证的。这就是CPython当前所做的。

一些相关功能是:

  • list_slice-简单切片(无步幅)
  • list_subscript-下标,公司。扩展切片(带步幅)
  • list_ass_slice-简单的切片分配(无步幅)
  • list_ass_subscript-下标分配公司。使用扩展切片

看看list的实现:https://github.com/python/cpython/blob/master/Objects/listobject.c

有一些有趣的花絮,例如code that protects against a[::-1] = a

答案 1 :(得分:1)

Python严格遵守可能的副作用。就语言而言,就地执行您的任何操作都是不正确

您的操作

 arr[2:] = arr[2:][::-1]

是三个单独的切片操作:

  • arr[2:]arr所有元素(但前两个)创建一个新列表。
  • ...[::-1]根据...所有元素创建一个新的反向列表。
  • arr[2:] = ...arr替换了...所有元素(但前两个)。

每个切片操作基本上等于原始O(n)复制操作。由于仅复制引用,因此元素的大小或类型无关紧要。

在您完整的示例中,在O(k)循环中这相当于三个O(n)切片操作:

ans = [i+1 for i in range(n)]   # O(n)
for i in range(k):              # O(k)
    ans[i:] = ans[i:][::-1]     # O(n)

总的来说,时间复杂度为O(nk)。空间复杂度仅为O(n),因为临时切片可立即回收。您基本上可以得到初始列表以及一些临时副本。


  

ans是字符串(例如ans = '12345...n')一样还是不同?

操作的复杂性不会改变-它们仍然是相同的原始操作。

实际上,CPython实现会欺骗某些对象。例如,+=inplace mutation for strings with refcount 1,即使字符串是不可变的。但是,这不适用于您的分片使用。

通常,在Python中依靠内置的优化是一个坏主意。


如果您担心空间不足,请从编写精益代码开始。例如,ans[i:][::-1]应该只是ans[i::-1]。仅此一项就将所需的临时空间减少了一半。