max*_*max 23 python cpython python-3.x python-internals
CPython的deque被实现为64项的双向链表大小的"块"(阵列).除了链表两端的那些块外,这些块都是满的.在IIUC中,当pop/ popleft删除块中的最后一项时,块被释放; 当append/ appendleft尝试添加新项目并且相关块已满时,将分配它们.
我理解使用链接列表而不是链接项列表所列出的优点:
malloc/ free添加/删除的每个项目执行/的运行时成本但是为什么不是首先使用单个动态大小的圆形数组而不是双链表呢?
AFAICT,圆形阵列将保留所有上述优点,并维持(atortized)成本pop*/ append*at O(1)(通过分配,就像在中list).此外,它还可以提高从当前O(n)到索引的索引查找成本O(1).循环数组也可以更简单地实现,因为它可以重用大部分list实现.
我可以在C++这样的语言中看到支持链表的论证,其中可以O(1)使用指针或迭代器从中间删除项目; 但是,python deque没有API来执行此操作.
Tim*_*ers 22
改编自我在python-dev邮件列表上的回复:
双端队列的主要目的是使两端的弹出和推动变得有效.这就是当前的实现方式:每次推送或弹出的最坏情况常量时间,无论deque中有多少项.在小型和大型中击败"摊销的O(1)".这就是为什么这样做的原因.
其他一些deque方法因此比列表慢,但是谁在乎呢?例如,我曾经用过双端队列的唯一索引是0和-1(窥视一端或另一端的双端队列),并且实现也使得访问这些特定索引的时间也是恒定的.
事实上,来自Raymond Hettinger的消息由Jim Fasarakis Hilliard在他的评论中引用:
https://www.mail-archive.com/python-dev@python.org/msg25024.html
证实了这一点
__getitem__投入的唯一原因是支持快速访问头部和尾部而不实际弹出值
除了接受@TimPeters 的回答之外,我还想添加一些不适合评论格式的额外观察结果。
让我们只关注一个常见的用例,其中 deque用作简单的 FIFO 队列。
一旦队列达到其峰值大小,循环数组就不再需要从堆中分配内存。我认为这是一个优势,但事实证明,CPython 实现通过保留可重用内存块的列表实现了相同的效果。一个领带。
随着队列大小的增长,循环数组和 CPython 都需要堆中的内存。CPython 需要一个简单的malloc,而数组需要(可能要贵得多)realloc(除非原始内存块的右边缘恰好有额外的空间可用,否则它需要释放旧内存并复制数据)。CPython 的优势。
如果队列的峰值比其稳定大小大得多,CPython 和数组实现都会浪费未使用的内存(前者将其保存在可重用的块列表中,后者将未使用的空白空间留在数组中) . 一个领带。
正如@TimPeters 指出的那样,每个 FIFO 队列 put / get 的成本始终O(1)用于 CPython,但仅O(1)针对数组进行摊销。CPython 的优势。
| 归档时间: |
|
| 查看次数: |
1159 次 |
| 最近记录: |