在Python中迭代字典的复杂性

在Python中迭代字典的复杂性,python,dictionary,time-complexity,Python,Dictionary,Time Complexity,这是一个相当简单的问题,我还没有找到答案。 如果我有一本字典,迭代它的复杂性是什么 换句话说,字典遍历的时间复杂度是多少,例如my_dict:print(key) 我天真的理解是,由于Python中的字典是哈希映射,我们需要迭代字典中所有可能的哈希值 这似乎有点过分了,但也许没关系,因为随着我们添加元素,字典会越来越大,所以我们总是通过拥有一个几乎满到恒定负载系数的字典来分摊成本。在大多数情况下,在字典上迭代总共需要O(n)个时间,或者平均需要O(1)个时间每个元素的时间,其中n是字典中的项目数

这是一个相当简单的问题,我还没有找到答案。 如果我有一本字典,迭代它的复杂性是什么

换句话说,字典遍历的时间复杂度是多少,例如my_dict:print(key)

我天真的理解是,由于Python中的字典是哈希映射,我们需要迭代字典中所有可能的哈希值


这似乎有点过分了,但也许没关系,因为随着我们添加元素,字典会越来越大,所以我们总是通过拥有一个几乎满到恒定负载系数的字典来分摊成本。

在大多数情况下,在字典上迭代总共需要O(n)个时间,或者平均需要O(1)个时间每个元素的时间,其中n是字典中的项目数

Python的字典数据结构有各种不同的版本,这取决于您使用的Python版本,但它们都是某种类型的。哈希表要么有一个键/值对数组,要么有一个键数组和一个并行值数组通常,数组的固定比例(称为)将包含字典项,其余的空格保持为空,因此需要迭代的数组长度是一个固定常数乘以字典项数。这意味着您可以在O(n)时间内进行迭代

,字典数据结构的数组只保存另一个数组中每个项的索引,其中另一个数组中的项按插入顺序保存。这个额外的数组可以用于按插入顺序在字典上迭代,仍然是O(n)时间,但不必跳过查找数组中未使用的空格

请注意,无论哪种方式,我们实际上都不需要计算任何键的散列来迭代字典的项


综上所述,在某些情况下,迭代字典可能需要超过O(n)个时间。这样做的原因是,尽管在需要插入更多项时哈希表的容量会增大,但在删除项时哈希表的容量不会缩小。(感谢@heapverflow在评论中指出了这一点。)

如果删除了许多项,那么字典项占阵列容量的比例可能比负载系数小得多。在这种情况下,数组可以大于固定常量乘以项数,因此迭代需要的时间超过O(n)

对于较新版本中使用的数据结构也是如此,它使用附加数组而不是查找数组进行迭代。删除项目时,只需将其替换为
NULL
();大概这样做是为了在保持插入顺序的同时,在O(1)时间内移除。因此,如果删除了许多项,则附加数组也可能长于O(n)


在大多数应用程序中,从字典中删除很多项并不常见;如果您需要这样做,并关心如何高效地迭代这些词典,请考虑使用只需要保留的关键字来构造新字典,而不是将它们从现有字典中删除。

迭代只是<代码> o(n)< /代码>。当您迭代键/项时,不涉及散列。例如,您的字典中有n个键,您需要迭代n个键,打印每个键,即取
O(n)
。在字典中,值由键索引,因此
dict[key]
O(1)
…除非发生冲突。如果所有键都有相同的散列,则最多需要
O(n)
。但这不太可能。@HeapOverflow字典项的数量。我认为这很清楚,但我想有人可能会把它和底层数组的长度混淆,所以我编辑了。是的,这正是我问的原因。因为它并不总是包含项目的固定部分。我可以很容易地生成一个包含0个条目的字典,它的迭代时间比包含9999个条目的字典要长得多:@HeapOverflow很好,谢谢,我已经编辑过了。我有点惊讶的是,“compact dict”也会出现同样的行为,因为删除算法可以检查它是否有75%的空值,如果有,可以重新分配/重新索引。这将给删除一个O(1)摊销成本,就像插入一样,但它将保证O(n)迭代。我想优化删除大量项目的情况并不是目标。@HeapOverflow re:
dict.clear
-我做了一件愚蠢的事,试图通过读取源代码而不是测试来推导它。它看起来不像是在重新分配阵列,但我想我错过了什么。再次感谢。我刚刚记起来,并看到它实际上是在注释[2]中提到的。