如何在Python heapq中实现reduce-key功能?

时间:2009-09-23 12:23:52

标签: python heap

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

5 个答案:

答案 0 :(得分:34)

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

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

答案 1 :(得分:7)

Decrease-key是许多算法必不可少的操作(Dijkstra的算法,A *,OPTICS),我想知道为什么Python的内置优先级队列不支持它。

不幸的是,我无法下载math4tots的软件包。

但是,我能够找到Daniel Stutzbach的this实施。使用Python 3.5完美地为我工作。

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

答案 2 :(得分:5)

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. 

非常新,但可能充满了错误。

答案 3 :(得分:4)

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

即使您保留对每个子列表的引用并实际更改密钥,您仍然无法在日志时间内执行此操作。由于列表只是对一堆可变对象的引用,因此您可以尝试执行类似记住任务的原始顺序的操作:(在这种情况下,“do laundry”是原始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是线性时间),但遗憾的是我们不知道{do laundry“的索引在{ {1}}(在这种情况下task_list_heap为1)。

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

答案 4 :(得分:2)

可能没有必要拥有 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