这个列表代码的附加和连接的复杂性有何不同?

gar*_*may 10 python big-o time-complexity python-2.7

考虑以下用于形成千位数列表的方法.

def test1():
    l = []
    for i in range(1000):
        l = l + [i]
    return l


def test2():
    l = []
    for i in range(1000):
        l.append(i)    

print timeit.repeat(stmt=test1, number=100,repeat=2)
print timeit.repeat(stmt=test2, number=100,repeat=2)
Run Code Online (Sandbox Code Playgroud)

输出:

[0.30474191033602543, 0.3783786557587963]
[0.015134341605235302, 0.023081246200096328]
Run Code Online (Sandbox Code Playgroud)

为什么追加方法比连接好大约20倍.AFAIK追加具有O(1)复杂度,而连接具有O(k)复杂度.虽然K在这里是1.

我忽略了一些明显的事情吗?

Mar*_*ers 19

您每次通过连接创建一个新的列表对象.这需要将旧列表中的所有元素复制到一个新元素中,再加上一个元素.所以是的,使用的l = l + [i]是O(N)算法,而不是O(1).

至少,不要使用+连接; 使用+= 扩充连接,这list.extend()与重新分配到同一个引用相同:

def test3():
    l = []
    for i in range(1000):
        l += [i]  # or use l.extend([i])
    return l
Run Code Online (Sandbox Code Playgroud)

这会产生:

>>> print timeit.repeat(stmt=test1, number=100, repeat=2)
[0.1333179473876953, 0.12804388999938965]
>>> print timeit.repeat(stmt=test2, number=100, repeat=2)
[0.01052403450012207, 0.007989168167114258]
>>> print timeit.repeat(stmt=test3, number=100, repeat=2)
[0.013209104537963867, 0.011193037033081055]
Run Code Online (Sandbox Code Playgroud)

  • @ garg10may:和O(1)是性能的*级*,而不是精确的测量; 不同的O(1)算法之间的恒定时间仍然可以变化. (4认同)
  • @ garg10may:`+ =`必须做更多工作; 它必须通过可变数量的元素来调整现有对象的大小,因此涉及到一个循环.`.append()`知道总有1个元素要添加,简化了逻辑. (4认同)
  • @ garg10may:`list.append()`绝对是添加单个元素的最佳方法,毫无疑问. (2认同)