如何让 heapq 评估特定属性的堆?
Posted
技术标签:
【中文标题】如何让 heapq 评估特定属性的堆?【英文标题】:How to make heapq evaluate the heap off of a specific attribute? 【发布时间】:2011-04-26 15:41:51 【问题描述】:我希望拥有一堆对象,而不仅仅是数字。它们将具有堆可以排序的整数属性。在python中使用堆最简单的方法是heapq,但是在使用heapq时如何告诉它按特定属性排序呢?
【问题讨论】:
【参考方案1】:很遗憾,您不能,尽管这是一个经常被要求的功能。
一种选择是将(键,值)元组插入堆中。但是,如果值在比较时抛出异常(它们将在键之间存在关联的情况下进行比较),则这将不起作用。
第二种选择是在类中定义一个__lt__
(小于)方法,该方法将使用适当的属性来比较元素以进行排序。但是,如果对象是由另一个包创建的,或者如果您需要它们在程序的其他地方进行不同的比较,这可能是不可能的。
第三种选择是使用blist 模块中的sortedlist 类(免责声明:我是作者)。 sortedlist
的构造函数采用 key
参数,该参数允许您指定返回元素排序键的函数,类似于 list.sort
和 sorted
的 key
参数。
【讨论】:
我删除了我之前的评论,因为我与blist
的问题可能是 PEBCAK(再次感谢您的模块),所以我只复制了之前评论的第一部分:总是可以定义一个通过子类化或封装使用__lt__
的类。【参考方案2】:
heapq
对对象进行排序的方式与 list.sort
相同,因此只需在您的类定义中定义一个方法 __cmp__()
,它将自己与同一类的另一个实例进行比较:
def __cmp__(self, other):
return cmp(self.intAttribute, other.intAttribute)
适用于 Python 2.x。
在 3.x 中使用:
def __lt__(self, other):
return self.intAttribute < other.intAttribute
【讨论】:
__cmp__
在 3.x 中消失了。请改用__lt__
。
__lt__
也适用于 Python 2,因此最好完全避免使用 __cmp__
。
正如您可以告诉任何排序基于对象自然排序以外的标准进行排序(例如,cmp
和key
用于sort
),您应该能够告诉@ 987654333@ 根据不同的键进行排序。换句话说,您不必重新定义对象本身来更改保存它的特定数据结构;你应该能够告诉数据结构本身。这是heapq
API 中缺少的一个值得注意的基本部分。
大家有什么理由要求使用__lt__
而不是__gt__
?还是真的没关系?
如果有时我想按这个属性排序,有时又按另一个属性排序怎么办?【参考方案3】:
根据documentation的例子,你可以使用元组,它会按照元组的第一个元素排序:
>>> h = []
>>> heappush(h, (5, 'write code'))
>>> heappush(h, (7, 'release product'))
>>> heappush(h, (1, 'write spec'))
>>> heappush(h, (3, 'create tests'))
>>> heappop(h)
(1, 'write spec')
因此,如果您不想(或不能?)执行__cmp__
方法,您可以在推送时手动提取排序键。
请注意,如果一对元组中的第一个元素相等,则将比较后面的元素。如果这不是您想要的,您需要确保每个第一个元素都是唯一的。
【讨论】:
"请注意,如果一对元组中的第一个元素相等,则将比较后面的元素。"你应该把它加粗,因为在文档中并不清楚。我假设给予相同的优先级它会返回我找到的第一个对象(没有充分的理由假设,所以这是我的错,我明白了)。 好点。如果插入的元组是 (number, dict) 它不知道如何评估 dicts。 如果你有一个像(some_value, dict)
这样的元组,你可以在堆中插入(some_value, counter, dict)
以打破与递增计数器的联系,以防some_value
等于2 个元组。
这个例子对我不起作用。有什么建议? lst = [(18, [3, 3]), (26, [5, -1]), (20, [-2, 4])] heapq.heapify(lst)【参考方案4】:
根据Official Document,解决方案是将条目存储为元组(请查看8.4.1 和8.4.2 节)。
例如,您的对象在 tuple 的格式中是这样的 (key, value_1, value_2)
当你把对象(即tuples)放入heap时,它会取对象中的第一个属性(在这种情况下是key) 进行比较。如果出现平局,堆将使用下一个属性(即 value_1)等等。
例如:
import heapq
heap = []
heapq.heappush(heap, (0,'one', 1))
heapq.heappush(heap, (1,'two', 11))
heapq.heappush(heap, (1, 'two', 2))
heapq.heappush(heap, (1, 'one', 3))
heapq.heappush(heap, (1,'two', 3))
heapq.heappush(heap, (1,'one', 4))
heapq.heappush(heap, (1,'two', 5))
heapq.heappush(heap, (1,'one', 1))
show_tree(heap)
输出:
(0, 'one', 1)
(1, 'one', 1) (1, 'one', 4)
(1, 'one', 3) (1, 'two', 3) (1, 'two', 2) (1, 'two', 5)
(1, 'two', 11)
关于在 python 中漂亮地打印一个堆(更新了链接):show_tree()
【讨论】:
【参考方案5】:你可以实现一个 heapdict。请注意使用 popitem() 来获取最低优先级的项目。
import heapdict as hd
import string
import numpy as np
h = hd.heapdict()
keys = [char for char in string.ascii_lowercase[:10]]
vals = [i for i in np.random.randint(0,10, 10)]
for k,v in zip(keys,vals):
h[k] = v
for i in range(len(vals)):
print h.popitem()
【讨论】:
【参考方案6】:我有同样的问题,但上述答案都没有到位,尽管有些答案很接近但不够详细。无论如何,我做了一些研究并尝试了这段代码,希望这对于下一个希望得到答案的人来说已经足够了:
使用元组的问题是它只使用第一项,这不是很灵活。我想要类似于 c++ 中的 std::priority_queue 的东西,如下所示:
std::priority_queue<pair<int, int>, vector<pair<int, int>>, comparator> pq;
我可以设计自己的比较器,这在现实世界的应用中更常见。
希望下面的 sn-p 帮助: https://repl.it/@gururajks/EvenAccurateCylinders
import heapq
class PQNode:
def __init__(self, key, value):
self.key = key
self.value = value
# compares the second value
def __lt__(self, other):
return self.value < other.value
def __str__(self):
return str(" : ".format(self.key, self.value))
input = [PQNode(1, 4), PQNode(7, 4), PQNode(6, 9), PQNode(2, 5)]
hinput = []
for item in input:
heapq.heappush(hinput, item)
while (hinput):
print (heapq.heappop(hinput))
【讨论】:
我尝试了你的代码,它对我有用。我正在使用 python 3.6.5。我很好奇 heappush() 如何进行比较。这是由 PQNode 类中的特殊 _lt_() 方法本质上完成的吗?没有它,该程序肯定会因编译器消息而崩溃: Traceback (last most recent call last): File "heap_example.py", line 18, in我觉得最简单的方法是覆盖 heapq 模块现有的 cmp_lt 函数。一个简短的例子:
import heapq
# your custom function. Here, comparing tuples a and b based on their 2nd element
def new_cmp_lt(self,a,b):
return a[1]<b[1]
#override the existing "cmp_lt" module function with your function
heapq.cmp_lt=new_cmp_lt
#Now use everything like normally used
【讨论】:
【参考方案8】:有一个模块叫做heaps
。 Github 地址是https://github.com/gekco/heapy。您可以在类的实例化或从数组创建堆时应用自己的键/排序函数,这非常有用,因为这样可以节省您在每次执行操作时将其添加为参数。
我想要列表的示例,元组最后一个位置的最小元素在堆顶部:
>>> from heapy.heap import Heap
>>> a = [(3, 5, 10), (-5, 3, 8), (7, 8, 9), (-4, 0, 2)]
>>> x = Heap.from_array(a, key=lambda t : t[-1])
>>> x.length
4
>>> x.top()
(-4, 0, 2)
>>> x.insert((-1, 0, 1))
>>> x.length
5
>>> x.top()
(-1, 0, 1)
>>> a
[(3, 5, 10), (-5, 3, 8), (7, 8, 9), (-4, 0, 2)]
【讨论】:
以上是关于如何让 heapq 评估特定属性的堆?的主要内容,如果未能解决你的问题,请参考以下文章