有时候有一个按键排序的字典是有意义的。在C ++中,这通常使用红黑树实现。但任何自我平衡的二元搜索树都会这样做(fwiw,Knuth在这个问题上特别清楚)。到目前为止我能够提出的最好的解决方案是使用R. McGraw's AVL-tree type并创建一个基本上实现STL映射接口的包装类(也依赖于Python中对的方便排序(两个元素元组)) )。这样的元组基本上对应于std :: map :: value_type。
是的,有Python的bisect模块,虽然在插入时是对数的,但是在插入时自平衡二进制树是对数的(对吧?),坦率地说我只想要一个对象。被称为OrderedDict或其他东西(不,Python 3.1 OrderedDict没有资格 - 这是'插入时'的排序 - 坦率地说,插入时间排序与排序有什么关系并不是很明显。)
请注意,按键排序的字典在许多行业中非常有用(例如,在财务方面,跟踪数据的价格表是很平常的,这些数据基本上是订购的价格词典 - >数量,汇总订单信息等等)。
如果有人有任何其他想法,那就太好了。我所知道的是Alex Martelli在这里的“答案”让我只有500万倍的智慧。所以我想我会问。
答案 0 :(得分:2)
我有完全相同的需求,Alex Martelli的回答完全让我信服:最好是保留字典和部分排序的键列表,然后在需要时排序。这是有效的,因为python的排序算法(AKA Timsort)非常特殊。 Key-ordered dict in Python
我测试了他的实现和我的,他是最好的(因为他没有插入列表中间)
(我强烈建议你阅读AM关于timsort的评论中链接的论文,这是一颗珍珠)。
答案 1 :(得分:1)
列表是树的悲惨替代品。
插入需要移动整个列表以腾出空间;删除需要将列表向下移动。在可能的情况下批量添加或删除东西是正常的,但通常不会,或者采取非自然的扭曲来安排它。树的基本属性是插入和删除是O(log n);任何数量的handwaving都不会将O(n)转换为O(log n)。
当您已经知道它将去往何处时,将项目插入树中是O(1)。等效地,基于其节点从树中删除项目也是O(1)。 std :: map支持这两种方式。这些都是带有列表的O(n)。
树的另一个基本属性是迭代一系列值是每次迭代O(1)。组合列表和字典会失去这个,因为每次迭代都需要进行字典查找。 (元组列表方法没有这个问题。)
树是最基本的数据类型之一。 Python缺少树容器类型是一个疣。也许有一个第三方库实现了一个(例如,由“未知”先生链接的那个,我没有尝试过,所以我不能担保),但是没有标准的Python类型。
答案 2 :(得分:1)
我偶然发现了这个需要OrderedMap的问题,我惊恐地发现接受的答案是完全垃圾。所以我自己动手,万一有人发现它有用:
from bisect import *
class OrderedMap:
"""Much less efficient than a dict,
but keys don't need to be hashable."""
__default_arg = object()
def __init__(self, keyvalues_iter = None):
self.clear()
if keyvalues_iter is not None:
self.update(keyvalues_iter)
def clear(self):
self.__keys = []
self.__values = []
def __index(self, key):
if self.__keys:
index = bisect(self.__keys, key)-1
if self.__keys[index] == key:
return index
raise KeyError(key)
def __len__(self):
return len(self.__keys)
def __contains__(self, key):
try:
self.__index(key)
return True
except KeyError:
return False
def __getitem__(self, key):
index = self.__index(key)
return self.__values[index]
def __setitem__(self, key, value):
try:
index = self.__index(key)
# exists
self.__values[index] = value
except KeyError:
# new
index = bisect(self.__keys, key)
self.__keys.insert(index, key)
self.__values.insert(index, value)
def __delitem__(self, key):
index = self.__index(key)
self.__keys.pop(index)
self.__values.pop(index)
def __iter__(self):
return iter(self.__keys)
def get(self, key, default=__default_arg):
try:
return self[key]
except KeyError:
if default != OrderedMap.__default_arg:
return default
raise
def setdefault(self, key, default = None):
try:
return self[key]
except KeyError:
if default != OrderedMap.__default_arg:
self[key] = default
return default
raise
def items(self):
return zip(self.__keys, self.__values)
def iteritems(self):
return iter((self.__keys[x], self.__values[x])
for x in xrange(len(self)))
def keys(self):
return self.__keys[:]
def iterkeys(self):
return iter(self.__keys)
def values(self):
return self.__values[:]
def itervalues(self):
return iter(self.__values)
def update(self, other):
for k, v in other.iteritems():
self[k] = v
def __repr__(self):
s = ", ".join("%s: %s" % (repr(self.__keys[x]),
repr(self.__values[x]))
for x in xrange(len(self)))
return "OrderedMap{%s}" % (s,)
答案 3 :(得分:1)
您可能正在寻找SortedCollection: http://code.activestate.com/recipes/577197-sortedcollection/
答案 4 :(得分:0)
对于保持排序的列表,您可以尝试模块heapq。
答案 5 :(得分:0)
Python sortedcontainers模块提供了SortedDict数据类型,正是出于这些目的。它使用修改后的B树类型数据结构,并使用纯Python编写。该模块具有100%的测试覆盖率和数小时的压力。虽然是纯Python,但它比C实现更快,并且有performance comparison来支持它。
因为它是纯Python,所以使用pip轻松安装:
pip install sortedcontainers
然后你只需:
from sortedcontainers import SortedDict
help(SortedDict)
performance comparison包含一个非常全面的替代实现列表。值得一看。
答案 6 :(得分:-1)
正如您所说,您可以使用bisect滚动您自己的实现:
class OrderedDict:
def __init__(self, keyvalues_iter):
self.__srtlst__ = sorted(keyvalues_iter)
def __len__(self):
return len(self.__srtlst__)
def __contains__(self, key):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
return True
else:
return False
def __getitem__(self, key):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
return self.__srtlst__[index][1]
else:
raise KeyError(key)
def __setitem__(sekf, key, value):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
self.__srtlst__[index][1] = value
else:
self.__srtlst__[index]=(key, value)
def __delitem__(sekf, key, value):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
del __srtlst__[index]
else:
raise KeyError(key)
def __iter__(self):
return (v for k,v in self.__srtlst__)
def clear(self):
self.__srtlst__ = []
def get(self, key, default=None):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
return self.__srtlst__[index][1]
else:
return default
def items(self):
return self.__srtlst__[:]
def iteritems(self):
return iter(self.__srtlst__)
def iterkeys(self):
return (k for k,v in self.__srtlst__)
def itervalues(self):
return (v for k,v in self.__srtlst__)
def keys(self):
return [k for k,v in self.__srtlst__]
def values(self):
return [v for k,v in self.__srtlst__]
def setdefault(self, key, default):
index = bisect(self.__srtlst__, key)
if index<len(self.__srtlst__) and self.__srtlst__[index][0] == key:
return self.__srtlst__[index][1]
else:
self.__srtlst__[index]=(key, default)
return default
def update(self, other):
#a more efficient implementation could be done merging the sorted lists
for k, v in other.iteritems():
self[k] = v
嗯......好像我已经为你做了那个,哦!哦!