典型解决方案
通常的解决方案是将一个元素标记为无效并插入一个新元素,然后在弹出无效条目时消除它们。
替代解决方案
如果这种方法还不够,只要知道要更改的值的位置,就可以在 O(log n) 步骤中恢复最小堆不变量。
回想一下,最小堆是使用两个原语构建和维护的,“siftup”和“siftdown”(尽管各种来源对哪个向上和哪个向下有不同的看法)。其中一个将值向下推到树下,另一个将它们向上浮动。
案例一:价值增加
如果新值x1大于旧值x0 ,则只需修复x下的树,因为parent(x) <= x0 < x1
. 只需将x与它的两个孩子中较小的一个交换,而x比它的一个孩子大,就可以将x向下推。
案例2:价值减少
如果新值x1小于旧值x ,则x下面的树不需要调整,因为x1 < x0 <= either_child(x)
. 相反,我们只需要向上移动,在x小于其父级时将x与其父级交换。不需要考虑同级节点,因为它们已经大于或等于可能会被较低值替换的父节点。
案例 3:值不变
不需要工作。现有的不变量不变。
Python中的工作代码
测试 1,000,000 次试验:创建一个随机堆。更改随机选择的值。恢复堆状态。验证结果是否为最小堆。
from heapq import _siftup, _siftdown, heapify
from random import random, randrange, choice
def is_minheap(arr):
return all(arr[i] >= arr[(i-1)//2] for i in range(1, len(arr)))
n = 40
trials = 1_000_000
for _ in range(trials):
# Create a random heap
data = [random() for i in range(n)]
heapify(data)
# Randomly alter a heap element
i = randrange(n)
x0 = data[i]
x1 = data[i] = choice(data)
# Restore the heap
if x1 > x0: # value is increased
_siftup(data, i)
elif x1 < x0: # value is decreased
_siftdown(data, 0, i)
# Verify the results
assert is_minheap(data), direction