Python 如何使heapq计算特定属性的堆?

Python 如何使heapq计算特定属性的堆?,python,data-structures,heap,Python,Data Structures,Heap,我希望拥有一堆物品,而不仅仅是数字。它们将包含一个整数属性,堆可以根据该属性进行排序。在python中使用堆的最简单方法是heapq,但是我如何告诉它在使用heapq时按特定属性排序呢?不幸的是,您不能,尽管这是经常需要的特性 一种选择是将(键、值)元组插入堆中。但是,如果值在比较时抛出异常,则这将不起作用(在键之间出现平局的情况下,将对它们进行比较) 第二个选项是在类中定义一个\uu___(小于)方法,该方法将使用适当的属性来比较元素以进行排序。但是,如果对象是由另一个包创建的,或者如果您需要

我希望拥有一堆物品,而不仅仅是数字。它们将包含一个整数属性,堆可以根据该属性进行排序。在python中使用堆的最简单方法是heapq,但是我如何告诉它在使用heapq时按特定属性排序呢?

不幸的是,您不能,尽管这是经常需要的特性

一种选择是将(键、值)元组插入堆中。但是,如果值在比较时抛出异常,则这将不起作用(在键之间出现平局的情况下,将对它们进行比较)

第二个选项是在类中定义一个
\uu___
(小于)方法,该方法将使用适当的属性来比较元素以进行排序。但是,如果对象是由另一个包创建的,或者如果您需要在程序中的其他地方对它们进行不同的比较,那么这可能是不可能的


第三种选择是使用模块中的类(免责声明:我是作者)。
sortedlist
的构造函数采用一个
key
参数,该参数允许您指定一个函数来返回元素的排序键,类似于
list的
key
参数。sort
sorted
heapq
以与
list.sort
相同的方式对对象进行排序,因此,只需在类定义中定义一个方法
\uuuu cmp\uuuu()
,它将自身与同一类的另一个实例进行比较:

def __cmp__(self, other):
    return cmp(self.intAttribute, other.intAttribute)
在Python2.x中工作

在3.x中使用:

def __lt__(self, other):
    return self.intAttribute < other.intAttribute
def\uu lt\uuu(自身、其他):
返回self.intAttribute
根据中的示例,您可以使用元组,它将按元组的第一个元素排序:

>>> 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')
因此,如果您不想(或不能?)执行
\uuu\cmp\uu
方法,您可以在推送时手动提取排序键


请注意,如果一对元组中的第一个元素相等,则会比较其他元素。如果这不是您想要的,您需要确保每个第一个元素都是唯一的。

根据,解决方案是将条目存储为元组(请参阅8.4.18.4.2

例如,您的对象在元组格式中类似于此 (键,值_1,值_2)

当您将对象(即元组)放入堆中时,将使用对象中的第一个属性(在本例中为key)进行比较。如果出现平局,堆将使用下一个属性(即值_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)
输出:

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打印堆(更新了链接):

您可以实现一个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()

我有同样的问题,但上面的答案都不中肯,尽管有些答案很接近,但不够详细。无论如何,我做了一些研究并尝试了这段代码,希望这对下一个想要得到答案的人来说足够了:

使用元组的问题是它只使用第一个项,这不是很灵活。我想要类似于STD的东西:C++中的PrimyItQueQueGe:
std::优先级队列pq
在那里,我可以设计自己的比较器,这在现实世界的应用中更为常见

希望下面的代码片段能有所帮助:

导入heapq
类节点:
定义初始化(自、键、值):
self.key=key
自我价值=价值
#比较第二个值
定义(自身、其他):
返回self.value
我觉得最简单的方法是覆盖heapq模块的现有cmp\u 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
导入heapq
#您的自定义函数。这里,根据元组a和元组b的第二个元素比较元组a和元组b
def new_cmp_lt(自身、a、b):

返回一个[1]
\uuuu cmp\uuuu
在3.x中消失的值。使用
\uuuu lt\uuuu
\uuuu lt\uuuu
也适用于Python 2,因此最好完全避免使用
\uuu cmp\uuuu
。正如您可以告诉任何排序都基于对象自然排序以外的标准进行排序一样(例如
cmp
排序的
),您应该能够告诉
heapq
根据不同的键进行排序。换句话说,您不必重新定义对象本身来更改保存它的特定数据结构;您应该能够知道数据结构本身。这是
heapq
API中缺少的一个重要的基本部分。是否有任何理由每个人都要求使用
\uult\uuuu
而不是
\ugt\uuuuu
?或者这真的不重要?如果有时我想按此属性排序,有时又想按另一个属性排序呢?我删除了以前的评论,因为我的
blist
问题可能是PEBCAK(再次感谢您的模块),因此,我只重复前面评论的第一部分:通过子类化或封装,始终可以使用
\uu lt\uuu
定义一个类。“请注意,如果一对元组中的第一个元素相等,则会比较更多的元素。”您应该用粗体表示,因为文档中不清楚。我假设在相同的优先级下,它会返回我找到的第一个对象(这个假设没有充分的理由,所以这是我的错,我明白了)。这一点很好。如果你插入一个元组(number,dict),它不知道如何计算dict。如果你有一个像
(some\u value,dict)
这样的元组,你可以在堆中插入
(some\u value,counter,dict)
,以打破与递增计数器的联系,以防
某些值
等于2个元组。这个例子对我不起作用。有什么建议吗?lst=[(18,[3,3