问题描述
我有一个二进制最小堆优先级队列的工作代码(具有所需的效率),该队列使索引0为空,并且根节点位于索引1(子级为2i和2i + 1)。我将新值插入到堆末尾第一个可用的空白处的堆中,然后应用heapify-up(如果需要)将其移至其各自的位置。这是教科书规定的技术。
但是,在我的作业中,教授希望我们将新值插入索引0,然后应用heapify-down将其移至其相应位置,但根索引仍应位于索引1 。
当从索引1(extractMin)中删除一个项目时,也将使用heapify-down方法的代码,并且必须四处移动项目以维护优先级队列和二进制最小堆结构。在这种情况下,索引0处没有任何项目...所有值都在索引> = 1处。
在这些情况下,是否可以为使用heapify-down方法保持O(log n)的效率?我应该创建2个heapify down方法吗?目前,我的方法大约有30行代码,并且为了修复该代码以满足分配要求,我的代码越来越多,几乎包含了100行以上的代码,其中包含大量的if / else语句。
感谢您的帮助!
更新:与教授交谈,他告诉我,他只是希望我们使用堆中的所有索引,因此不会浪费空间。我应该不理会他先前关于在索引0 + heapify-down插入的电子邮件和分配的详细信息,而只需将索引0用作swap方法。目前,我正在使用一个temp变量,所以交换过程分3个步骤完成,但是我正在按照他的说明修改它,以利用arr [0]处的空间-现在是4个步骤,但它满足了他的要求:-)
解决方法
如您所言,extractMin()
可以以传统方式实现,我将重点介绍如何实现自上而下的add(elt)
方法。
保证O(log n)性能的一个关键方面是使堆数组紧凑,以便具有n
元素且索引为1的根的堆始终将这些元素存储在位置1到{ {1}}(含)。通常,这是通过在索引n
处添加一个新元素,然后对其进行渗滤直到恢复heap属性来实现的。为了自上而下执行此操作,我们要考虑沿相同的路径,但从上到下而不是从下到上。无论哪种方式,顶点集都是相同的,并且可以通过将目标顶点索引n + 1
依次减半来确定。使用您选择的名称作为使用Python的默认权限,以下简单函数生成给定参数n
的索引集:
n
例如,运行def index_path(n):
bits = n.bit_length()
return [n >> (bits - i) for i in range(1,bits)]
会产生index_path(42)
,您可以很容易地确认这与使用自下而上方法评估的是同一组索引。只要我们坚持通过堆的这条路,就可以保持紧凑性。
然后是算法
- 将新元素放置在数组的索引0
- 更新
[1,2,5,10,21]
,即堆中的元素数量 - 生成从顶部(1)一直到但不包括最后一个索引(
n
)的索引集 - 遍历索引集
- 如果当前迭代值等于或小于0 th 值,则前进到下一个;
- 否则,将0 th 值替换为当前迭代器的值
- 在索引
n
上附加或插入(视情况而定)第0 th 值
Python的快速实现:
n
这是一个测试运行:
class MyHeap:
def __init__(self):
self.ary = [None]
self.size = 0
def add(self,elt):
self.ary[0] = elt
self.size += 1
for index in index_path(self.size):
if self.ary[0] < self.ary[index]:
self.ary[0],self.ary[index] = self.ary[index],self.ary[0]
if len(self.ary) > self.size:
self.ary[self.size] = self.ary[0]
else:
self.ary.append(self.ary[0])
self.inspect()
def inspect(self):
print(self.ary,self.size)
产生:
test_data = [42,42,96,17,1,3,29,12,39]
test_heap = MyHeap()
test_heap.inspect()
for x in test_data:
test_heap.add(x)
可以很容易地在每个阶段将其确认为有效堆。
与自下而上的方法不同,此版本不能短路。每次都必须经过整个路径。