我了解字典是insertion ordered in Python 3.6+,在3.6中有实现细节,在3.7+中是官方。
鉴于它们是有序的,因此似乎不存在通过插入顺序检索字典中第 i th 项的方法,这似乎很奇怪。可用的only solutions似乎具有O( n )复杂度,或者:
list.__getitem__
。enumerate
字典项在循环中,并在达到所需索引时返回值。同样,时间复杂度为O( n )。由于从list
获取项目的复杂度为O(1),有没有办法用字典实现相同的复杂度?常规dict
或collections.OrderedDict
都可以。
如果不可能,是否有结构上的原因阻止这种方法,或者这仅仅是尚未考虑/实现的功能?
答案 0 :(得分:35)
对于OrderedDict
,它固有地是O(n)
,因为顺序记录在linked list中。
对于内置dict,有一个向量(一个连续的数组)而不是一个链表,但最后几乎是一样的:向量包含几种“假人”,特殊的内部值表示“没有密钥已存储在此处”或“曾经存储在此处但不再存储的密钥”。例如,这使得删除密钥非常便宜(只需用一个伪值覆盖密钥)。
但是,如果不在其上添加辅助数据结构,就无法跳过虚拟变量而不一次移动它们。因为Python使用一种开放式寻址的形式来解决冲突,并将负载因子保持在2/3以下,所以向量的条目 中至少有三分之一是虚拟变量。 the_vector[i]
可以在O(1)
时间访问,但实际上与第i个非虚拟条目没有可预测的关系。
答案 1 :(得分:3)
根据@TimPeters' answer,由于结构原因,您无法在O(1)时间内按位置访问字典项。
如果要通过键或位置查找O(1)查找,则值得考虑其他选择。有第三方库(例如NumPy / Pandas)提供了这样的功能,对于不需要指针的数字数组,尤其是高效的。
使用Pandas,您可以构建具有类似标签的“字典式”系列,并通过“标签”或位置提供O(1)查找。您牺牲的是删除标签时的性能,这会产生O( n )成本,就像list
一样。
import pandas as pd
s = pd.Series(list(range(n)))
# O(n) item deletion
del s[i]
s.drop(i)
s.pop(i)
# O(1) lookup by label
s.loc[i]
s.at[i]
s.get(i)
s[i]
# O(1) lookup by position
s.iloc[i]
s.iat[i]
pd.Series
绝不是dict
的直接替代品。例如,如果该系列主要用作映射,则不会阻止重复的键,并且会导致问题。但是,如上例所示,将数据存储在连续的内存块中,您可能会看到性能上的显着提高。
另请参阅: