簡體   English   中英

遍歷 Python 中的雙端隊列的時間復雜度是多少?

[英]What is the time complexity of iterating through a deque in Python?

通過 Python 中的 collections 庫中的雙端隊列進行迭代的時間復雜度是多少?

一個例子是這樣的:

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

每次迭代都是常數 O(1) 操作嗎? 還是在每次迭代中執行線性 O(n) 操作來獲取元素?

網上有很多關於時間復雜度的資源,以及所有其他雙端隊列方法,如appendleftappendpopleftpop 似乎沒有關於雙端隊列迭代的任何時間復雜度信息。

謝謝!

如果您的構造是這樣的:

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

沒有遍歷deque ,而是遍歷了range對象, 然后索引到了deque 由於每次索引操作, elements[i]為O(n),因此使迭代次數為多項式時間。 但是,實際上對deque 迭代是線性時間。

for x in elements:
    print(x)

這是一個快速的經驗檢驗:

import 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()

和結果情節: 在此處輸入圖片說明

現在,我們實際上可以看到如何在CPython源代碼中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;

因此,如評論中所述, deque是“塊”節點的雙向鏈接列表,其中,塊本質上是python對象指針的數組。 現在使用迭代器協議:

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;
}

...

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;
}

正如您所看到的,迭代器本質上跟蹤着隊列索引,指向塊的指針以及雙端隊列中所有項目的計數器。 如果計數器達到零,它將停止迭代,否則計數器將停止獲取當前索引處的元素,然后遞增索引,使計數器遞減,然后檢查是否要移至下一個塊。 換句話說,雙端隊列可以表示為Python中的列表列表,例如d = [[1,2,3],[4,5,6]]並進行迭代

for block in d:
    for x in block:
        ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM