在 Python 中合并两个列表的运行时间

Runtime of merging two lists in Python

假设我们有两个列表A = [a1, a2, ..., an](n个元素)和B = [b1, b2, ..., bm](m个元素),我们在Python中使用“+”将两个列表合并为一个,所以

C = A + B;

我的问题是这个操作的运行时间是多少?我的第一个猜测是 O(n+m),不确定 Python 是否比它更聪明。

当您使用 A + B 连接两个列表时,您会在内存中创建一个全新的列表。这意味着您的猜测是正确的:复杂度为 O(n + m)(其中 nm 是列表的长度)因为 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.

的开销

here 在 Python wiki 上列出了各种列表操作的复杂性。

复制列表是O(n)(n 是元素的数量)并且扩展是O(k)(n 是第二个列表中的元素的数量)。基于这两个事实,我认为它不会少于O(n+k),因为这是一个复制和扩展操作,最起码你需要复制两个列表的所有元素。

来源:Python TimeComplexity

My first guess is O(n+m), not sure if Python is smarter than that.

Nothing 在返回 copy 时比这更聪明。虽然即使 AB 是字符串等不可变序列; CPython 仍然制作完整副本,而不是为相同的内存设置别名(它简化了此类字符串的垃圾回收的实现)。

在某些特定情况下,操作可能是 O(1),具体取决于您要对结果执行的操作,例如,itertools.chain(A, B) 允许遍历所有项目(它不会制作副本, AB 的变化会影响产出的物品)。或者,如果您需要随机访问;您可以使用 Sequence 子类来模拟它,例如 WeightedPopulation 但在一般情况下,副本和因此 O(n+m) 运行时是不可避免的。