【问题标题】:Python: Update value of element in heapqPython:更新堆中元素的值
【发布时间】:2014-10-08 17:42:51
【问题描述】:

如果我有一个包含以下元素的 heapq:

import heapq


class Element(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val

if __name__ == "__main__":
    heap = []
    e1 = Element('A', 1)
    e2 = Element('B', 65)
    e3 = Element('C', 53)
    e4 = Element('D', 67)
    ...

    heapq.heappush(heap, e1)
    heapq.heappush(heap, e2)
    heapq.heappush(heap, e3)
    heapq.heappush(heap, e4)
    ...

    #IF I want to take elements from the heap and print them I will call:
    while heap:
        new_e = heapq.heappop(heap)
        print new_e.name + ' ' + str(new_e.val)

假设我在堆上有 50 个元素。我想将元素 e3 的值从 val = 53 更改为 val = 0。所以这不是堆的顶部元素。我也不想从堆中删除其他元素。 我怎样才能进行这样的更新?

【问题讨论】:

  • 使用heapq 制作优先级队列的可能解决方案是right there in the documentation
  • 您的Elements 无法比较,所以我不确定您如何将它们与heapq 一起使用。您需要一个 __lt__ 方法(或使用已经具有可比性的内置类型,例如 tuple)。
  • 哦,我明白了,在 Python 2 中,所有对象都是可比较的,如果您不定义 __cmp__ 或某些丰富的比较方法,则可以随意排序。不过,这个问题仍然有些荒谬,因为 Element 实例的 val 属性与其在堆中的位置完全没有关系。

标签: python python-2.7 queue heap priority-queue


【解决方案1】:

确定所需元素的索引后,您可以在整个堆上调用heapify 来恢复堆。识别索引是困难的部分,因为您需要在每次堆操作后跟踪每个元素在堆中的位置,我认为最简单的方法就是自定义实现堆。

import heapq
h = [[1, "A"], [65, "B"], [53, "C"], [67, "D"]]
heapq.heapify(h)
# assume you know index of element C is 2
h[2][0] = 0
heapq.heapify(h)

有趣的是,CPython's heapq implementation 有内部方法 _siftup_siftdown。在实践中使用_siftup 应该比在整个堆上调用heapify 更快,但时间复杂度不高,因为heapify 将调用_siftup n/2 次,但实际上应该只交换log(n) 个元素。

【讨论】:

    【解决方案2】:

    这是一个老问题,但万一有人在未来看到这个并正在寻找答案......

    Python3 的 heapq 的新实现包括一些关于如何更新堆元素的有用说明,基本上将其用作优先级队列。 https://docs.python.org/3.5/library/heapq.html#priority-queue-implementation-notes 本质上,您可以创建一堆元组,Python 将根据元组的顺序比较来评估优先级。由于 Python 中的堆基本上只是一个标准列表,顶部使用了 heapq 接口,因此文档建议可能有一个额外的字典,将您的堆值映射到堆中的索引(列表)。

    所以对于你原来的问题:

    假设我在堆上有 50 个元素。我想改变价值 元素 e3 从 val = 53 到 val = 0。所以这不是顶部元素 堆的。我也不想从堆中删除其他元素。 我怎样才能进行这样的更新?

    按照上述逻辑更新堆中元素的基本步骤是:

    • 查看字典获取要更新的元素的索引(检查元素在字典+对应堆后)
    • 更新堆中的值
    • 调用 heapq.heapify(heap),它在 O(N) 中运行。或者,如果您知道您的更新会将值更新 +/- 1,您可能只是将您尝试更新的元素的位置与它上方或下方的元素交换。

    编辑:这是一个类似的问题,有更多答案:How to update elements within a heap? (priority queue)

    【讨论】:

    • 关于“调用 heapq.heapify(heap) 在 O(N) 中运行。”:重新平衡应该只取 O(log n) 或树的最大深度,并且过程是排序的喜欢你关于交换父母/子女职位的建议。但是,在某些情况下,如果没有错误,“将值更新 +/-1”会产生误导。特别是,如果优先级值不必是唯一的,或者优先级值不是整数(如浮点数),则这是错误的。
    • 但是在使用堆时如何保持将元素映射到其索引的字典同步?
    【解决方案3】:

    很难运行您的代码,因为没有输出。但是,我尝试了一些方法:

    在 heapq 模块中,heap[0] 总是被指定为最小的项。在您的情况下, 1 是最小的项目。因此,将这个值从 1 更改为 5 理论上应该很容易。我尝试了heapq.heappop(heap),它应该返回最小值。因此,正如您在问题中所说,“我想更新元素的 val 我不知道它是哪个”,此方法会自动为您获取最小值(我从您的问题中假设您想要替换 1因为它是最小值,因为它与名称 First 相结合)但是,当我尝试自己运行您的代码时,我收到了 <__main__.Element object at 0x103c15dd0> 所以您应该尝试修复您的代码以便您可以打印输出,同样的事情发生对于print heap[0],错误类型相同。然后,一旦您不再收到此类错误,请在代码块的末尾尝试:

    s = heapq.heappop(heap)
    
    print heapq.heapreplace(5, s)
    

    使用这种方法,我得到以下错误:TypeError: heap argument must be a list 因此,如果您能弄清楚如何将 s 转换为列表,那么这应该可以工作。也许有人可以编辑我的答案以添加此代码。

    希望这会有所帮助。

    自行编辑:

    将此添加到代码块的末尾,[] 将其变成一个列表,这就是 heapq 想要的输入。

    s = heapq.heappop(heap)
    
    print heapq.heapreplace([5], [s])
    

    这将在输出中返回值 5。

    回到输出问题,如果你指定了你希望输出的样子,我可以尝试为你提供更多帮助。

    【讨论】:

    • 你误会了我的问题。我进行了编辑以更好地解释我的问题,并尝试展示如何打印有关元素的信息。代码运行良好。
    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 2018-04-10
    • 2019-05-15
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多