在Python中迭代双端队列的时间复杂度是多少?

时间:2017-11-16 20:31:09

标签: python python-3.x python-collections

迭代的时间复杂度是多少,或者更准确地说,是通过Python中的集合库中的双端队列进行的每次迭代?

一个例子是:

elements = deque([1,2,3,4])
for element in elements:
  print(element)

每次迭代是否为常数O(1)运算?或者它是否进行线性O(n)运算以在每次迭代中到达元素?

appendleftappendpopleftpop等所有其他双端方法相比,网上有许多资源可用于时间复杂度。关于双端队列的迭代,似乎没有时间复杂性信息。

谢谢!

1 个答案:

答案 0 :(得分:4)

如果您的构造类似于:

elements = deque([1,2,3,4])
for i in range(len(elements)):
    print(elements[i])

没有迭代deque ,你正在迭代range对象,然后索引到{{1} }。这使得迭代多项式时间,因为每个索引操作deque是O(n)。但是,实际上迭代超过elements[i]是线性时间。

deque

这是一个快速的实证测试:

for x in elements:
    print(x)

由此产生的情节: enter image description here

现在,我们实际上可以看到CPython source codeimport timeit import pandas as pd from collections import deque def build_deque(n): return deque(range(n)) def iter_index(d): for i in range(len(d)): d[i] def iter_it(d): for x in d: x r = range(100, 10001, 100) index_runs = [timeit.timeit('iter_index(d)', 'from __main__ import build_deque, iter_index, iter_it; d = build_deque({})'.format(n), number=1000) for n in r] it_runs = [timeit.timeit('iter_it(d)', 'from __main__ import build_deque, iter_index, iter_it; d = build_deque({})'.format(n), number=1000) for n in r] df = pd.DataFrame({'index':index_runs, 'iter':it_runs}, index=r) df.plot() 个对象如何实现迭代器协议:

首先,deque对象本身:

deque

因此,正如评论中所述,typedef struct BLOCK { struct BLOCK *leftlink; PyObject *data[BLOCKLEN]; struct BLOCK *rightlink; } block; typedef struct { PyObject_VAR_HEAD block *leftblock; block *rightblock; Py_ssize_t leftindex; /* 0 <= leftindex < BLOCKLEN */ Py_ssize_t rightindex; /* 0 <= rightindex < BLOCKLEN */ size_t state; /* incremented whenever the indices move */ Py_ssize_t maxlen; PyObject *weakreflist; } dequeobject; 是“块”节点的双向链接列表,其中块本质上是一个python对象指针数组。现在为迭代器协议:

deque

...

typedef struct {
    PyObject_HEAD
    block *b;
    Py_ssize_t index;
    dequeobject *deque;
    size_t state;          /* state when the iterator is created */
    Py_ssize_t counter;    /* number of items remaining for iteration */
} dequeiterobject;

static PyTypeObject dequeiter_type;

static PyObject *
deque_iter(dequeobject *deque)
{
    dequeiterobject *it;

    it = PyObject_GC_New(dequeiterobject, &dequeiter_type);
    if (it == NULL)
        return NULL;
    it->b = deque->leftblock;
    it->index = deque->leftindex;
    Py_INCREF(deque);
    it->deque = deque;
    it->state = deque->state;
    it->counter = Py_SIZE(deque);
    PyObject_GC_Track(it);
    return (PyObject *)it;
}

正如您所看到的,迭代器基本上跟踪块索引,指向块的指针以及deque中总项的计数器。如果计数器达到零,它将停止迭代,如果不是,它会抓取当前索引处的元素,递增索引,递减计数器,并故意检查是否移动到下一个块。换句话说,A deque可以表示为Python中的列表列表,例如static PyObject * dequeiter_next(dequeiterobject *it) { PyObject *item; if (it->deque->state != it->state) { it->counter = 0; PyErr_SetString(PyExc_RuntimeError, "deque mutated during iteration"); return NULL; } if (it->counter == 0) return NULL; assert (!(it->b == it->deque->rightblock && it->index > it->deque->rightindex)); item = it->b->data[it->index]; it->index++; it->counter--; if (it->index == BLOCKLEN && it->counter > 0) { CHECK_NOT_END(it->b->rightlink); it->b = it->b->rightlink; it->index = 0; } Py_INCREF(item); return item; } ,并迭代

d = [[1,2,3],[4,5,6]]