为什么添加到列表的前面需要二次时间?

时间:2014-05-01 16:00:43

标签: python list

此代码示例取大O(N ^ 2)

results = []

for i in range(1000000)
    result = [f(i)] + results

此代码示例采用(N)

的Big O.
results = []

for i in range(1000000)
    result = results + [f(i)]

为什么这两种算法的Big O有这么明显的区别,唯一的区别是一个被添加到列表的前面而另一个被添加到列表的后面?

这对Java也适用吗?

2 个答案:

答案 0 :(得分:7)

因为列表已针对追加进行了优化,而不是预先添加:如果您预先添加,则需要重新创建整个列表。

如果您希望数据结构能够以相同的效率追加到两端,请使用collections.deque

答案 1 :(得分:3)

这种情况的原因是因为CPython在C中实现并使用C数组作为列表的后备存储,并且必须在将元素添加到列表前面之前重新分配和移动元素。

要回答问题的后半部分,"这也适用于Java吗?"答案是肯定的,Java中的原始数组和Java中的ArrayList都不允许在不重新分配和移动元素的情况下预先添加到列表中。

此外,您的代码存在缺陷,因为您要分配给result而不是results,因此您实际上并未导致问题中所述的二次性能。演示此行为的更好方法如下:

results = []
for i in range(1000000):
    results.insert(0, f(i))

附加版本:

results = []
for i in range(1000000):
    results.append(f(i))

或使用timeit模块:

$ python -m timeit 'results = []' 'for i in range(10000): results.insert(0, i)'
10 loops, best of 3: 50.9 msec per loop
$ python -m timeit 'results = []' 'for i in range(10000): results.append(i)'
1000 loops, best of 3: 794 usec per loop