我有一个像这样的堆(python,heapq模块) -
>>> h = []
>>> heappush(h, (5, 'write code'))
>>> heappush(h, (7, 'release product'))
>>> heappush(h, (1, 'write spec'))
>>> heappush(h, (3, 'create tests'))
如何使用项目值删除元组为"创建测试"在O(logn)中保存堆属性?
这是我能想到的(不是O(logn))
for i in range(len(h)):
if h[i][1] == "create tests":
h[i], h[-1] = h[-1], h[i]
popped = h.pop()
heapq.heapify(h)
break
答案 0 :(得分:10)
如果您确实需要从heap
中取出一件物品,但想要保留heap
,则可以懒散地将其丢弃并在物品自然出现时将其丢弃,而不是搜索列表为了它。
如果您要将要删除的项目存储在黑名单set
中,则每次heapq.heappop
检查该项目是否在set
中。如果它存在则丢弃它并再次heappop
,直到您收到未列入黑名单的内容,或者heap
为空
答案 1 :(得分:2)
如果多个已删除的元素具有相同的值,则黑名单集会有问题。而是使用tombstone-counting-dict实现heap_remove
:
def heap_remove(heap, value):
tombstones[value] = tombstones.get(value, 0) + 1
while len(heap) and heap[0] in tombstones and tombstones[heap[0]]:
heappop(heap)
正如预期的那样,您已经分摊了O(1)删除时间,并且只要您不是从其他地方的top
{»}},您的堆的popping
始终是准确的。
考虑这个数字列表,首先将它们全部推入堆中,然后以相同的顺序“删除”(不弹出):
[3,3,2,7,1,4,2]
插入按预期工作:
After inserting 3 into heap, top = 3
After inserting 3 into heap, top = 3
After inserting 2 into heap, top = 2
After inserting 7 into heap, top = 2
After inserting 1 into heap, top = 1
After inserting 4 into heap, top = 1
After inserting 2 into heap, top = 1
但删除是通过递增对象的墓碑来完成的。如果堆的顶部设置了墓碑,则删除该对象并递减tomstone计数器。
Called remove( 3 )
Marking 3 for deletion
Called remove( 3 )
Marking 3 for deletion
Called remove( 2 )
Marking 2 for deletion
Called remove( 7 )
Marking 7 for deletion
Called remove( 1 )
Marking 1 for deletion
Deleting top 1
Updated heap is: [2, 2, 3, 7, 3, 4]
Deleting top 1
Updated heap is: [2, 3, 3, 7, 4]
Called remove( 4 )
Marking 4 for deletion
Called remove( 2 )
Marking 2 for deletion
Deleting top 2
Updated heap is: [3, 3, 4, 7]
Deleting top 3
Updated heap is: [3, 7, 4]
Deleting top 3
Updated heap is: [4, 7]
Deleting top 4
Updated heap is: [7]
Deleting top 7
Updated heap is: []
请注意,当第二个heap_remove(3)
被调用时,@ GP89的解决方案会因为墓碑集中已有3
而崩溃。
您可以浏览此示例here。
答案 2 :(得分:0)
我担心只有heapq没有这样的方法。由于从堆中搜索元素需要O(n)
。
但您可以将其与dict
之类的内容结合使用,这样可以O(1)
次来搜索条目。
<强>更新:强>
我尝试使用dict进行簿记,但是如何在插入时获得“创建测试”的索引? - Prakhar 3小时前
一种天真的方法是:
# remember to update this hdict when updating the heap.
hdict = { h[i][1]: i for i in range(len(h)) }
然后,您只需访问此hdict
而不是O(n)
线性搜索,即可获取给定字符串的索引。
答案 3 :(得分:0)
有2个以上的想法, 这是一个完整的演示:我将使其简洁明了。
from heapq import heappush, heappop
class Solution:
def demo():
deleted = {}
h = [0]
heappush(h, 789)
heappush(h, 101)
heappush(h, 101)
self.remove(h, 101, deleted)
max_val = self.peek(h, deleted)
def remove(self, h, y, deleted):
deleted[y] = deleted.get(y, 0) + 1
while len(h) > 0 and h[0] == y and deleted[y] > 0:
heappop(h)
deleted[y] -= 1
def peek(self, h, deleted):
while len(h) > 0 and deleted.get(h[0],0) > 0:
deleted[h[0]] -= 1
heappop(h)
return h[0]
答案 4 :(得分:0)
通过这种方法,我基本上是跟踪字典中的元素。因此,每当我删除它时,搜索过程就会变成O(1)。
class RemoveHeap:
def __init__(self):
self.h = []
self.track = collections.defaultdict(collections.deque)
self.counter = itertools.count()
def insert_item(self, val):
count = next(self.counter)
item = [val, count, 'active']
self.track[val].append(item)
heapq.heappush(self.h, item)
def delete_item(self, val):
if val in self.track:
items = self.track[val]
for item in items:
if item[2] == 'active':
item[2] = 'deleted'
break
def pop_item(self):
while len(self.h) > 0:
item = heapq.heappop(self.h)
item_track = self.track[item[0]]
item_track.popleft()
if len(item_track) == 0:
del self.track[item[0]]
else:
self.track[item[0]] = item_track
if item[2] == 'active':
return item[0]
def peek_item(self):
item = self.h[0]
if item[2] == 'deleted':
x = self.pop_item()
self.insert_item(x)
return x
return item[0]