【问题标题】:How can I implement decrease-key functionality in Python's heapq?如何在 Python 的 heapq 中实现减少键功能?
【发布时间】:2010-11-30 17:48:04
【问题描述】:

我知道可以在 O(log n) 中实现减少键功能,但我不知道如何?

【问题讨论】:

    标签: python heap


    【解决方案1】:

    要有效地实现“减少键”,您需要访问“减少此元素并与子元素交换此元素,直到堆条件恢复”的功能。在heapq.py 中,这称为_siftdown(类似地_siftup 用于递增)。所以好消息是这些函数就在那里......坏消息是它们的名称以下划线开头,表明它们被认为是“内部实现细节”,不应由应用程序代码直接访问(下一个版本的标准库可能会使用此类“内部”来改变和破坏代码)。

    由您决定是否要忽略警告前导-_,使用 O(N) heapify 而不是 O(log N) 筛选,或者重新实现 heapq 的部分或全部功能以使筛选原语“作为接口的公共部分公开”。由于 heapq 的数据结构已记录在案并且是公开的(只是一个列表),我认为最好的选择可能是部分重新实现——本质上,将筛选函数从 heapq.py 复制到您的应用程序代码中。

    【讨论】:

    • heapq.py 的链接似乎已过时。为方便起见,这里是 python 实现的另一个链接:hg.python.org/cpython/file/2.7/Lib/heapq.py
    • 你的意思是“交换这个元素和它的 parent 直到堆条件恢复”? (我假设如果有元素,[2, 3, 5],那么 2 将是父元素,35 将是它的两个子元素)
    • 应该注意的是,即使你可以实现“decrease-key”或更通用的“update-key”,该功能假定你有一种方法可以跟踪堆上的索引,这样你可以查明您要操作的项目(否则您可能必须在线性时间内搜索它)。第一个明显的解决方案是使用 key-to-index hashmap 来扩充你的堆结构。从那时起,堆更改操作(例如_siftup_siftdown)应该会触发地图的更新。
    【解决方案2】:

    Decrease-key是很多算法(Dijkstra's Algorithm、A*、OPTICS)的必备操作,不知道为什么Python内置的优先级队列不支持。

    很遗憾,我无法下载 math4tots 的软件包。

    但是,我找到了 Daniel Stutzbach 的 this 实现。使用 Python 3.5 非常适合我。

    hd = heapdict()
    hd[obj1] = priority
    hd[obj1] = lower_priority
    # ...
    obj = hd.pop()
    

    【讨论】:

    【解决方案3】:

    heapq documentation 有一个关于如何执行此操作的条目。

    但是,我已经编写了一个 heap 包,它正是这样做的(它是 heapq 的包装器)。所以如果你有pipeasy_install 你可以做类似的事情

    pip install heap
    

    然后在你的代码中写

    from heap.heap import heap
    
    h = heap()
    
    h['hello'] = 4 # Insert item with priority 4.
    
    h['hello'] = 2 # Update priority/decrease-key has same syntax as insert. 
    

    虽然它很新,所以可能充满了错误。

    【讨论】:

      【解决方案4】:

      假设您将堆用作优先级队列,其中有一堆由字符串表示的任务,每个任务都有一个键。具体来说,请查看:task_list = [[7,"do laundry"], [3, "clean room"], [6, "call parents"]] 其中task_list 中的每个任务都是一个带有优先级和描述的列表。如果你运行heapq.heapify(task_list),你会让你的数组保持堆不变。但是,如果您想将“洗衣服”的优先级更改为 1,如果没有对堆进行线性扫描,您将无法知道“洗衣服”在堆中的位置(因此不能在对数时间内执行 reduce_key) .注意decrease_key(heap, i, new_key) 要求您知道要在堆中更改的值的索引。

      即使您维护对每个子列表的引用并实际更改密钥,您仍然无法在日志时间内完成。由于列表只是对一组可变对象的引用,您可以尝试执行类似记住任务的原始顺序的操作:(在这种情况下,“洗衣服”是您原始 task_list 中的第 0 个任务):

      task_list = [[7, "do laundry"], [3, "clean room"], [6, "call parents"]]
      task_list_heap = task_list[:] # make a non-deep copy
      heapq.heapify(task_list_heap)
      # at this point:
      # task_list = [[7, 'do laundry'], [3, 'clean room'], [6, 'call parents']]
      # task_list_heap = [3, 'clean room'], [7, 'do laundry'], [6, 'call parents']]
      # Change key of first item of task_list (which was "do laundry") from 7 to 1.
      task_list[0][0] = 1
      # Now:
      # task_list = [[1, 'do laundry'], [3, 'clean room'], [6, 'call parents']]
      # task_list_heap = [3, 'clean room'], [1, 'do laundry'], [6, 'call parents']]
      # task_list_heap violates heap invariant at the moment
      

      但是,您现在需要调用heapq._siftdown(task_list_heap, 1) 来保持堆在日志时间(heapq.heapify 是线性时间)中的不变性,但不幸的是我们不知道task_list_heap 中“洗衣服”的索引( heap_index 在这种情况下是 1)。

      所以我们需要实现我们的堆来跟踪每个对象的heap_index;例如,有一个 list(用于堆)和一个 dict 将每个对象映射到其在堆/列表中的索引(随着堆位置的交换而更新,为每个交换添加一个常数因子)。您可以通读heapq.py 并自行实施,因为过程很简单;但是,其他人已经实现了这种HeapDict

      【讨论】:

        【解决方案5】:

        可能没有必要拥有decrease_key 函数(尽管拥有它很好)。

        无论如何,您都可以将您的(priority, item) 推入优先队列,然后使用set 来检查您是否已经看到它。例如:

        pq = []  # heapq is a min heap
        seen = set()
        heappush(pq, (2, "item1"))
        heappush(pq, (3, "item2"))
        heappush(pq, (1, "item3"))
        heappush(pq, (4, "item4"))
        heappush(pq, (2, "item2"))
        
        while pq:
            p, item = heappop(pq)
            if item not in seen:
                seen.add(item)
                print(item, p)
            else:
                print(item, "is already handled with a higher priority!")
        

        输出是:

        item3 1
        item1 2
        item2 2
        item2 is already handled with a higher priority!
        item4 4
        

        【讨论】:

          【解决方案6】:

          C++ 和 Java 标准库优先级队列也缺少此功能。标准的解决方法是推送一个新的键值对,并隐式或显式地将原始键值对标记为无效。见How to update elements within a heap? (priority queue)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-19
            • 2012-03-18
            • 2011-05-14
            • 2019-08-11
            • 1970-01-01
            • 1970-01-01
            • 2020-11-01
            • 2012-02-24
            相关资源
            最近更新 更多