我测试了两种不同的方法来反转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
这是为什么?在操作方面,将元件插入头部似乎并不昂贵。
答案 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)你总是追加到列表的前面。