这个问题与此处提出的另一个问题有关: Sorting 1M records
我已经弄清楚了排序时遇到的问题。每次更新数据时,我都会将字典中的项目排序到列表中。我已经意识到Python排序的很多功能在于它可以更快地对已经部分排序的数据进行排序。
所以,这是问题所在。假设我有以下作为样本集:
self.sorted_records = [(1, 1234567890), (20, 1245678903),
(40, 1256789034), (70, 1278903456)]
列表中每个元组的 t[1]
是唯一ID。现在我想用下面的内容更新这个列表:
updated_records = {1245678903:45, 1278903456:76}
最快的方式是以最快的方式完成
self.sorted_records = [(1, 1234567890), (45, 1245678903),
(40, 1256789034), (76, 1278903456)]
目前我正在做这样的事情:
updated_keys = updated_records.keys()
for i, record in enumerate(self.sorted_data):
if record[1] in updated_keys:
updated_keys.remove(record[1])
self.sorted_data[i] = (updated_records[record[1]], record[1])
但我确信那里有一个更快,更优雅的解决方案。
任何帮助?
*编辑 事实证明我使用了错误的exids,因为当我进行更新时它们按排序顺序排列。我实际上对t [0]按排序顺序感兴趣。在我进行更新后,我打算使用更新的数据,但看起来bisect可能是按排序顺序插入的票证。 结束编辑*
答案 0 :(得分:2)
您正在扫描所有n条记录。您可以改为执行二进制搜索,即O(log(n))而不是O(n)。您可以使用bisect
模块执行此操作。
答案 1 :(得分:1)
因为显然你并不关心self.sorted_records
实际被排序的结束值(你有1号,45号,20号,76号的值 - 那是没有排序的! - ),你似乎只关心updated_records
中同样位于self.sorted_data
的ID,listcomp(如果你想动态更改updated_record,还有副作用)会很好地为你服务,即:
self.sorted_data = [(updated_records.pop(recid, value), recid)
for (value, recid) in self.sorted_data]
.pop
调用会从updated_records
中删除最终在新self.sorted_data
中的键(和相应的值)(以及“recid
的前一个值” ,value
,作为pop的第二个参数提供,以确保在updated_record
}中recid不在的地方没有变化;这留下了updated_record
“新”的内容,所以你可以在重新排序之前将其附加到self.sorted_data
,即我怀疑你想要继续使用类似
self.sorted_data.extend(value, recid
for recid, value in updated_records.iteritems())
self.sorted_data.sort()
虽然这部分超出了你实际问的问题(而且我只是因为我已经看过你的以前的问题而给出了这些问题; - )。
答案 2 :(得分:1)
这里你可能最好通过某种形式的树来维护(保留排序顺序,同时允许O(log n)替换。)没有内置的balanaced树类型,但你可以找到很多第三方的例子。或者,你可以:
使用二进制搜索来查找节点。 bisect模块将执行此操作,但它会根据正常的python比较顺序进行比较,而您似乎根据每个元组的第二个元素进行排序。您可以撤消此操作,或者只编写您自己的二进制搜索(或者只是从bisect_left获取代码并对其进行修改)
同时使用dict 和列表。该列表仅包含已排序的键。您可以轻松地将dict类包装起来以确保它保持同步。这允许您在保持键的排序顺序的同时快速更新字典。这可以防止由于dict / list之间的持续转换而导致排序性能丢失的问题。
这是一个快速实现这样的事情:
import bisect
class SortedDict(dict):
"""Dictionary which is iterable in sorted order.
O(n) sorted iteration
O(1) lookup
O(log n) replacement ( but O(n) insertion or new items)
"""
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self._keys = sorted(dict.iterkeys(self))
def __setitem__(self, key, val):
if key not in self:
# New key - need to add to list of keys.
pos = bisect.bisect_left(self._keys, key)
self._keys.insert(pos, key)
dict.__setitem__(self, key, val)
def __delitem__(self, key):
if key in self:
pos = bisect.bisect_left(self._keys, key)
del self._keys[pos]
dict.__delitem__(self, key)
def __iter__(self):
for k in self._keys: yield k
iterkeys = __iter__
def iteritems(self):
for k in self._keys: yield (k, self[k])
def itervalues(self):
for k in self._keys: yield self[k]
def update(self, other):
dict.update(self, other)
self._keys = sorted(dict.iterkeys(self)) # Rebuild (faster if lots of changes made - may be slower if only minor changes to large dict)
def keys(self): return list(self.iterkeys())
def values(self): return list(self.itervalues())
def items(self): return list(self.iteritems())
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, ', '.join("%s=%r" % (k, self[k]) for k in self))
答案 3 :(得分:0)
由于你想用字典键替换,但是按字典值排序数组,你肯定需要线性搜索键。从这个意义上讲,您的算法是您所希望的最佳算法。
如果要保留旧字典值,则可以使用二进制搜索值,然后在二进制搜索引导的位置附近找到密钥。