具有自定义比较谓词的heapq

vse*_*har 61 python sorting algorithm containers dictionary

我正在尝试使用自定义排序谓词构建堆.由于进入它的值是'用户定义'类型,我无法修改它们的内置比较谓词.

有没有办法做这样的事情:

h = heapq.heapify([...], key=my_lt_pred)
h = heapq.heappush(h, key=my_lt_pred)
Run Code Online (Sandbox Code Playgroud)

或者甚至更好,我可以将heapq函数包装在我自己的容器中,这样我就不需要继续传递谓词了.

jsb*_*eno 92

根据heapq文档,自定义堆顺序的方法是使堆上的每个元素都成为元组,第一个元组元素是接受普通Python比较的元素.

heapq模块中的函数有点麻烦(因为它们不是面向对象的),并且总是要求我们的堆对象(堆化列表)作为第一个参数显式传递.我们可以通过创建一个非常简单的包装类来一举两得,它将允许我们指定一个key函数,并将堆作为一个对象呈现.

下面的类保留了一个内部列表,其中每个元素都是一个元组,其中第一个成员是一个键,使用key参数在元素插入时计算,在Heap实例化时传递:

# -*- coding: utf-8 -*-
import heapq

class MyHeap(object):
   def __init__(self, initial=None, key=lambda x:x):
       self.key = key
       if initial:
           self._data = [(key(item), item) for item in initial]
           heapq.heapify(self._data)
       else:
           self._data = []

   def push(self, item):
       heapq.heappush(self._data, (self.key(item), item))

   def pop(self):
       return heapq.heappop(self._data)[1]
Run Code Online (Sandbox Code Playgroud)

  • 如果元素不可比较并且键值之间存在联系,这将失败。我将 `id(item)` 作为元组的中间元素来打破关系。 (7认同)
  • 非常好!您甚至可以更进一步,使用三元组(self.key(item),id,item),其中id可以是作为类属性处理的整数,并且在每次按下后递增。这样,您可以避免在key(item1)= key(item2)时引发异常。因为密钥是唯一的。 (4认同)
  • 我实际上试图将其(或基于此的东西)推入Python的stdlib中,但该建议被拒绝了。 (2认同)

Fan*_*Bao 82

定义一个类,在其中覆盖__lt__()函数。请参见下面的示例(适用于 Python 3.7):

import heapq

class Node(object):
    def __init__(self, val: int):
        self.val = val

    def __repr__(self):
        return f'Node value: {self.val}'

    def __lt__(self, other):
        return self.val < other.val

heap = [Node(2), Node(0), Node(1), Node(4), Node(2)]
heapq.heapify(heap)
print(heap)  # output: [Node value: 0, Node value: 2, Node value: 1, Node value: 4, Node value: 2]

heapq.heappop(heap)
print(heap)  # output: [Node value: 1, Node value: 2, Node value: 2, Node value: 4]

Run Code Online (Sandbox Code Playgroud)

  • 这似乎是迄今为止最干净的解决方案! (10认同)
  • 在“heapq”中进行比较时,Python 首先查找“__lt__()”。如果未定义,它将查找“__gt__()”。如果两者都没有定义,则会抛出“TypeError: '&lt;' not support between 'Node' and 'Node'”。这可以通过定义`__lt__()`和`__gt__()`,在每个中放置一个打印语句,并让`__lt__()`返回`NotImplemented`来确认。 (6认同)
  • 我使用 `__gt__` 进行了测试,它也有效。为什么我们使用哪种魔法方法并不重要?我在“heapq”的文档中找不到任何内容。也许这与 Python 一般如何进行比较有关? (2认同)

srg*_*erg 12

所述heapq文档表明,堆元件可以是元组,其中所述第一元件是所述优先级,并限定的排序顺序.

然而,与您的问题更相关的是,文档中包含了一个示例代码讨论,该示例代码说明了如何实现自己的heapq包装函数来处理排序稳定性问题和具有相同优先级的元素(以及其他问题).

简而言之,他们的解决方案是让heapq中的每个元素都是具有优先级的三元组,一个条目计数和要插入的元素.条目计数确保具有相同优先级的元素按照它们添加到heapq的顺序排序.


小智 10

setattr(ListNode, "__lt__", lambda self, other: self.val <= other.val)
Run Code Online (Sandbox Code Playgroud)

使用它来比较 heapq 中对象的值

  • 避免重新定义/重新封装对象的有趣方法! (2认同)