假设我们有两个列表A = [a1, a2, ..., an]
(n个元素)和B = [b1, b2, ..., bm]
(m个元素),我们在Python中使用“+”将两个列表合并为一个,所以
C = A + B;
我的问题是这个操作的运行时是什么?我的第一个猜测是O(n+m)
,不确定Python是否比那更聪明。
答案 0 :(得分:6)
当您使用A + B
连接两个列表时,您将在内存中创建一个全新的列表。这意味着您的猜测是正确的:复杂性为O(n + m)
(其中n
和m
是列表的长度),因为Python必须依次遍历两个列表以构建新列表。
您可以在source code for Python lists:
的list_concat
函数中看到这种情况
static PyObject *
list_concat(PyListObject *a, PyObject *bb)
{
/* ...code snipped... */
src = a->ob_item;
dest = np->ob_item;
for (i = 0; i < Py_SIZE(a); i++) { /* walking list a */
PyObject *v = src[i];
Py_INCREF(v);
dest[i] = v;
}
src = b->ob_item;
dest = np->ob_item + Py_SIZE(a);
for (i = 0; i < Py_SIZE(b); i++) { /* walking list b */
PyObject *v = src[i];
Py_INCREF(v);
dest[i] = v;
}
/* ...code snipped... */
如果你不需要内存中的新列表,那么利用列表是可变的这一事实通常是个好主意(这就是Python 智能的地方)。在复杂性方面使用A.extend(B)
O(m)
意味着您可以避免复制列表a
的开销。
在Python维基上列出了各种列表操作的复杂性here。
答案 1 :(得分:2)
我的第一个猜测是
O(n+m)
,不确定Python是否比那更聪明。
在返回副本时,没有什么可以比这更聪明。虽然A
,B
是不可变的序列,如字符串; CPython仍然使用完整副本而不是别名相同的内存(它简化了对这些字符串的垃圾收集的实现)。
在某些特定情况下,操作可能是O(1)
,具体取决于您要对结果执行的操作,例如,itertools.chain(A, B)
允许迭代所有项目(它不会复制,更改在A
中,B
影响已产生的项目)。或者如果您需要随机访问;您可以使用Sequence
子类(例如WeightedPopulation
来模拟它,但在一般情况下,副本和O(n+m)
运行时是不可避免的。
答案 2 :(得分:0)
复制列表为O(n)
(n为元素数),扩展名为O(k)
(n为第二个列表中的元素数)。根据这两个事实,我认为它不能小于O(n+k)
,因为这是一个复制和扩展操作,至少你需要复制两个列表的所有元素。