我已经编写了基本的python片段,首先在列表中插入值然后反转它们。我发现insert和append方法之间的执行速度差异很大。
摘录1:
L = []
for i in range(10**5):
L.append(i)
L.reverse()
执行此操作所需的时间:
real 0m0.070s
user 0m0.064s
sys 0m0.008s
摘录2:
l = []
for i in range(10**5):
l.insert(0,i)
执行时间:
real 0m5.645s
user 0m5.516s
sys 0m0.020s
我希望代码片段2的性能比snippet1好得多,因为我之前通过插入数字直接执行反向操作。但是,所用的时间不然。我无法理解为什么后一种方法需要更多时间来执行,即使该方法看起来更优雅。有没有人对此有任何解释?
答案 0 :(得分:32)
以下是来自 Duncan Booth的完整answer :
列表由指向对象的指针数组实现 包含。
每次调用'insert(0,indx)'时,所有指针都已经存在 列表必须在新位置之前向上移动一次 在开头插入。
当你调用'append(indx)'时,只需要复制指针 当前分配的块中没有足够的空间用于新的 元件。如果有空间,则无需复制现有空间 元素,只需将新元素放在最后并更新长度 领域。每当必须为该特定分配新块时 append将不会比插入更快,但是会有一些额外的空间 如果您希望进一步扩展列表,则分配。
如果你希望插入更快,也许你认为Python 使用了链表实现。它没有这样做,因为在 实践(对于大多数应用程序)基于列表的实现给出 更好的表现。
我实际上没有别的东西可以补充。
答案 1 :(得分:16)
请注意,您的结果将取决于精确的Python实现。 cpython(和pypy)会自动调整列表大小和 overprovision 空间,以便将来添加,从而加快append
的速度。
在内部,列表只是具有恒定大小的内存块(在堆上)。有时你很幸运,可以增加块的大小,但在很多情况下,一个对象已经存在。例如,假设您为列表[a,b,c,d]
分配了大小为4的块,而另一段代码为字典分配了大小为6的块:
Memory 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|a b c d| | dictionary |
假设您的列表包含4个元素,并添加另一个元素。现在,您只需将列表大小调整为5:
Memory 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|a b c d e| dictionary |
但是,如果现在需要另一个元素,你会怎么做?
嗯,你唯一能做的就是获得一个新的空间并复制列表的内容。
Memory 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| dictionary |a b c d e f |
请注意,如果您批量获取空间(上述过度配置),您只需要不时地调整(并可能复制)列表。
相反,当您在位置0处插入时,您始终需要复制列表。我们插入x
:
Memory 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
orig |a b c d| |dictionary|
after |x a b c d|dictionary|
虽然最后有足够的空间来附加x,但我们不得不移动(甚至不是复制,内存可能更便宜)所有其他值。
答案 2 :(得分:5)
如果您需要的数据结构在开始时插入和插入一样有效,那么您应该考虑deque。
答案 3 :(得分:2)
我已经学会了在“Python Pocket Reference”中将x插入列表开头的技巧:
l[:0] = [x]
它必须与l.insert(0,x)非常相似,但是当我尝试比较三个选项时:append(x),insert(0,x)和l [:0] = [x],最后一个选项的执行速度比第二个选项快一点。
这是测试代码和结果
import time
def test():
n = 10**5
t0 = time.time()
l = []
for i in xrange(n): l.append(i)
t1 = time.time() - t0
print 'appending: %.5f' % t1
t0 = time.time()
l = []
for i in xrange(n): l.insert(0, i)
t2 = time.time() - t0
print 'insert to 0: %.5f' % t2
t0 = time.time()
l = []
for i in xrange(n): l[:0] = [i]
t3 = time.time() - t0
print 'set slice: %.5f' % t3
return t1, t2, t3
if __name__ == '__main__':
t = [0] * 3
ntimes = 10
for _ in xrange(ntimes):
ti = test()
for i in xrange(3):
t[i] += ti[i]
t = [i/ntimes for i in t]
print 'average time:', t
平均时间
[0.011755657196044923, 4.1943151950836182, 3.3254094839096071]
为什么它比插入(0,x)快25%?我试图交换估计t1,t2,t3的代码块,但结果是一样的,所以它不是关于缓存列表。
Here,它声明设置切片需要O(k + n)
答案 4 :(得分:0)
在Queue中适当实现插入方法。 FIFO操作,插入列表的前面。 例如:。 items.insert(0,项)
追加在堆栈中正确实现的方法。 LIFO操作,插入列表的末尾。 例如:。 items.append(项目)
当我们通过INSERT方法使用插入数据时,请确保重新排序所有索引。
答案 5 :(得分:0)
https://wiki.python.org/moin/TimeComplexity 去这里查看列表的所有方法及其时间复杂度