为什么l.insert(0,i)比python中的l.append(i)慢?

时间:2013-07-14 01:43:05

标签: python algorithm list reverse

我测试了两种不同的方法来反转python中的列表。

import timeit

value = [i for i in range(100)]
def rev1():
    v = []
    for i in value:
        v.append(i)
    v.reverse()

def rev2():
    v = []
    for i in value:
        v.insert(0, i)

print timeit.timeit(rev1)
print timeit.timeit(rev2)

有趣的是,将值插入第一个元素的第二个方法比第一个元素慢得多。

20.4851300716
73.5116429329

这是为什么?在操作方面,将元件插入头部似乎并不昂贵。

3 个答案:

答案 0 :(得分:11)

insert是一个O(n)操作,因为它要求插入位置或之后的所有元素都向上移动一个。另一方面,append通常为O(1)(在最坏的情况下,O(n),必须分配更多空间时。这解释了实质性的时差。

这些方法的时间复杂性已经完整记录here

我引用:

  

在内部,列表表示为数组;最大的成本来自于超出当前分配大小(因为一切都必须移动),或者在开头附近的某处插入或删除(因为之后的所有内容都必须移动)。

现在,回到您的代码,我们可以看到rev1()O(n)实现,而rev2()实际上是O(n2),所以rev2()有意义{{1}} 1}}会慢得多。

答案 1 :(得分:1)

在Python中,列表实现为数组。如果将一个元素附加到数组,则只需展开数组的保留空间。如果你在前面添加一个元素,那么所有元素都会移动1,这非常昂贵。

答案 2 :(得分:1)

你可以通过在线阅读python列表来确认这一点。 Python将列表实现为数组,其中数组的大小实际上通常大于当前列表的大小。未使用的元素位于数组的末尾,表示可以添加到列表的END而不是开头的新元素。 Python使用经典的摊销成本方法,因此平均而言,如果你做一堆追加,追加到列表的末尾需要花费O(1)时间,尽管偶尔单个追加会导致数组变满,所以一个新的更大的数组需要创建,并将所有数据复制到新数组。另一方面,如果始终插入列表的前面,那么在底层数组中,所有元素都需要移动到一个索引上,以便为数组开头的新元素腾出空间。因此,总而言之,如果您通过执行N次插入来创建列表,那么如果您始终将新项目附加到列表的末尾,则总运行时间将为O(N),如果您将新项目附加到列表的末尾,则它将为O(N ^ 2)你总是追加到列表的前面。