📅  最后修改于: 2023-12-03 14:49:02.676000             🧑  作者: Mango
在上一篇文章中,我们介绍了二项式堆的基本实现和插入操作。在本篇文章中,我们将继续介绍二项式堆的两个重要操作:delete()
和decreaseKey()
。这两个操作是实现优先队列、Dijkstra算法等常用算法的关键。
delete()操作用于在二项式堆中删除某个节点。我们需要首先找到要删除的节点,并将其从堆中移除。然后,我们需要将其子节点们和其他二项式树进行合并,以维护堆的性质。
具体实现方法如下:
首先找到要删除的节点,并将其从堆中移除。这意味着我们需要找到包含该节点的二项式树,并将其从该树中删除。
接着,我们需要将该节点的子节点们重新插入堆中。这可以通过将它们作为根节点,创建对应的新的二项式树来完成。
最后,我们需要将新的二项式树们合并为一颗新的二项式堆,以维护堆的性质。这可以通过将每个二项式树看作是一颗完整的堆,并且按照它们的阶数(即节点的度数)进行合并。
以下是delete()操作的Python代码实现:
class BinomialHeap:
# ...
def delete(self, val):
node = self.findNode(val)
if node is None:
return
self.decreaseKey(node, float('-inf'))
self.extractMin()
def findNode(self, val):
node = self.head
queue = [node]
while queue:
node = queue.pop(0)
if node.val == val:
return node
if node.child is not None:
queue.append(node.child)
if node.sibling is not None:
queue.append(node.sibling)
return None
在上述代码中,我们首先通过findNode(val)
方法找到要删除的节点。接着,我们将其关键字值减小到负无穷。由于堆的最小值始终位于堆的根节点中,因此我们可以通过extractMin()
方法将该节点从堆中移除。
最后,我们定义了findNode(val)
函数,用于在堆中查找具有指定关键字值的节点。这个函数使用了广度优先搜索(BFS)算法,在堆中遍历每个节点的子节点和兄弟节点。
decreaseKey()操作用于将二项式堆中的节点的关键字值减小。这个操作在Dijkstra算法等最短路径算法中经常使用。具体实现方法如下:
首先找到要修改关键字值的节点,并将其“标记”为已修改。这意味着我们需要将节点的mark
属性设置为True
。
如果节点成为了其他节点的子节点,则需要将其从父节点中移除,并将其重新插入堆中,以便保持堆的性质。
接着,我们需要递归向上遍历这个节点的父节点,将它们也“标记”为已修改。如果它们已经被“标记”了,我们需要将它们从父节点中移除,并将它们重新插入堆中。
最后,我们需要找到堆中的最小值,并将其返回。这可以通过遍历每个根节点,并选择具有最小关键字值的节点来完成。
以下是decreaseKey()操作的Python代码实现:
class BinomialHeap:
# ...
def decreaseKey(self, node, val):
if val > node.val:
return
node.val = val
parent = node.parent
if parent is not None and node.val < parent.val:
self.cut(node, parent)
self.cascadingCut(parent)
if node.val < self.minNode.val:
self.minNode = node
def cut(self, node, parent):
if parent.child == node:
parent.child = node.sibling
else:
sibling = parent.child
while sibling.sibling != node:
sibling = sibling.sibling
sibling.sibling = node.sibling
parent.degree -= 1
node.parent = None
node.sibling = self.head
self.head = node
def cascadingCut(self, node):
parent = node.parent
if parent is None:
return
if node.mark:
self.cut(node, parent)
self.cascadingCut(parent)
else:
node.mark = True
在上述代码中,我们首先检查新的关键字值是否大于原来的值。如果是,则不需要修改节点的关键字。否则,我们将修改关键字值,并检查节点是否需要被移动到其他节点的子树中。
如果该节点成为其他节点的子节点,则我们需要将它从父节点中移除,并将其重新插入堆中。
接着,我们需要递归向上遍历其父节点,并将其“标记”为已修改。这可以防止二项式堆的操作次数增多,例如在插入新节点时。
最后,我们需要找到堆中的最小节点,并将其更新为minNode
。
我们还定义了两个辅助函数:cut(node, parent)
和cascadingCut(node)
。这两个函数用于在执行decreaseKey()操作时,维护堆的性质。
在代码中,cut(node, parent)
函数用于将node从其父节点parent的兄弟链表中移除,并将其插入堆中。cascadingCut(node)
函数则用于处理一系列与node兄弟节点的关系。
通过这篇文章,你应该已经学会了在二项式堆中执行delete()和decreaseKey()操作的实现方法。现在,你已经具备了使用二项式堆进行优先队列、Dijkstra算法等最短路径问题的能力。
在下一篇文章中,我们将演示如何使用二项式堆进行最短路径问题的求解。