我知道可以在O(log n)中实现减少键功能,但我不知道怎么做?
答案 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
的包装)。因此,如果您有pip
或easy_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