导致我开发这个解决方案(我不是 100% 肯定是正确的)的原因是我实际上找到了一个解决方案来删除最小-最大堆中的任何节点,但这是错误的。
可以在此处(在 C++ 中实现)和此处(在 Python 中实现)找到错误的解决方案。我将介绍刚才提到的错误Python的解决方案,每个人都更容易理解:
解决方案如下:
def DeleteAt(self, position):
"""delete given position"""
self.heap[position] = self.heap[-1]
del(self.heap[-1])
self.TrickleDown(position)
现在,假设我们有以下最小-最大堆:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 55 65 37 31
据我检查,这是一个有效的最小最大堆。现在,假设我们要删除元素 55,它在从 0 开始的数组中将在索引 9 处找到(如果我计算正确的话)。
上面的解决方案只需将最后一个元素放入数组中,在本例中为 31,并将其放在位置 9:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37 55
它将删除数组的最后一个元素(现在是 55),生成的最小-最大堆如下所示:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37
最后它会从position
(即现在我们有数字 31 的地方)“涓涓细流”。
“trickle-down”将检查我们是否处于偶数(或最小)或奇数(或最大)级别:我们处于奇数级别(3),因此“trickle-down”将称为“trickle-down- max" 从 31 开始,但由于 31 没有孩子,所以它停止了(如果你不知道我在说什么,请查看上面的原始论文)。
但是,如果您观察到使数据结构处于不再是 min-max 堆的状态,因为 54 处于偶数级别,因此应该小于其后代,但大于其后代之一 31。
这让我觉得我们不能只看节点的子节点 at position
,但我们还需要从那里position
向上检查,也许我们也需要使用“涓涓细流”。
在下面的推理中,让我们删除我们想要删除的元素之后和任何修复操作运行之前的元素x
。position
让它p
成为它的父母(如果有的话)。
我的算法的想法就是这样,更具体地说,是基于以下事实:
Ifx
位于奇数级别(如上面的示例中),我们将其与p
位于偶数级别的 parent 交换,这不会破坏从 newx
的位置向下的 min-max 堆的任何规则/不变量.
这当然似乎不是我的全部解决方案,当然我还想检查x
ie的前一个父级p
是否处于正确位置。
把这些放在一起,我想出了解决方案:
function DELETE(H, i):
// H is the min-max heap array
// i is the index of the node we want to delete
// I assume, for simplicity,
// it's not out of the bounds of the array
if i is the last index of H:
remove and return H[i]
else:
l = get_last_index_of(H)
swap(H, i, l)
d = delete(H, l)
// d is the element we wanted to remove initially
// and was initially at position i
// So, at index i we now have what was the last element of H
push_up(H, i)
push_down(H, i)
return d
这似乎根据我制作的最小-最大堆的实现起作用,您可以在此处找到。
另请注意,解决方案在O(log 2 n)时间内运行,因为我们只是调用按该顺序运行的“上推”和“下推”。