“Random access O(1)”是一个非常严格的要求,它基本上强加了一个底层散列表——我希望您指的是仅随机读取,因为我认为它可以从数学上得到证明,而不是一般情况下不可能有O(1)写入和O(N)有序迭代。
我不认为你会找到一个适合你需要的预打包容器,因为它们是如此极端——O(log N)访问当然会在世界上造成所有的不同。要获得读取和迭代所需的big-O行为,需要粘合两个数据结构,基本上是dict和heap(或排序列表或树),并保持它们同步。虽然您没有指定,但我认为您只能得到您想要的那种分期偿还的行为-除非您真的愿意为插入和删除支付任何性能损失,这是您所表达的规范的字面含义,但看起来确实是一个不太可能的现实需求。
对于O(1)read和stalledO(N)ordered iteration,只需在dict旁边保留一个所有键的列表,例如:class Crazy(object):
def __init__(self):
self.d = {}
self.L = []
self.sorted = True
def __getitem__(self, k):
return self.d[k]
def __setitem__(self, k, v):
if k not in self.d:
self.L.append(k)
self.sorted = False
self.d[k] = v
def __delitem__(self, k):
del self.d[k]
self.L.remove(k)
def __iter__(self):
if not self.sorted:
self.L.sort()
self.sorted = True
return iter(self.L)
如果您不喜欢“分期O(N)顺序”,可以删除self.sorted,只需在__setitem__本身中重复self.L.sort()。当然,这会导致写O(N logn)(而我仍然在O(1)处有写操作)。两种方法都是可行的,很难认为其中一种方法在本质上优于另一种方法。如果你倾向于写一堆,然后是一堆迭代,那么上面代码中的方法是最好的;如果它通常是一个写,一个迭代,另一个写,另一个迭代,那么它只是一个清洗。
顺便说一句,这无耻地利用了Python sort(也称为“timsort”)不寻常的(和奇妙的;-)性能特性:其中,对一个列表进行排序,该列表大部分是排序的,但最后附加了一些额外的项,基本上是O(N)(如果附加的项与排序的前缀部分相比足够少)。我听说Java很快就获得了这类功能,因为Josh Block对一个关于Python的技术讲座印象深刻,他开始在笔记本电脑上为JVM编写代码。大多数系统(包括我认为Jython和IronPython)基本上都将排序作为一个O(N logn)操作,而不是利用“大多数是有序的”输入;“自然合并排序”(natural mergesort),Tim Peters在Python的timsort中创建的,在这方面是一个奇迹。