📜  二项式堆的实现|设置– 2(delete()和decreseKey())(1)

📅  最后修改于: 2023-12-03 14:49:02.676000             🧑  作者: Mango

二项式堆的实现|设置– 2(delete()和decreseKey())

在上一篇文章中,我们介绍了二项式堆的基本实现和插入操作。在本篇文章中,我们将继续介绍二项式堆的两个重要操作:delete()decreaseKey()。这两个操作是实现优先队列、Dijkstra算法等常用算法的关键。

1. delete()操作

delete()操作用于在二项式堆中删除某个节点。我们需要首先找到要删除的节点,并将其从堆中移除。然后,我们需要将其子节点们和其他二项式树进行合并,以维护堆的性质。

具体实现方法如下:

  1. 首先找到要删除的节点,并将其从堆中移除。这意味着我们需要找到包含该节点的二项式树,并将其从该树中删除。

  2. 接着,我们需要将该节点的子节点们重新插入堆中。这可以通过将它们作为根节点,创建对应的新的二项式树来完成。

  3. 最后,我们需要将新的二项式树们合并为一颗新的二项式堆,以维护堆的性质。这可以通过将每个二项式树看作是一颗完整的堆,并且按照它们的阶数(即节点的度数)进行合并。

以下是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)算法,在堆中遍历每个节点的子节点和兄弟节点。

2. decreaseKey()操作

decreaseKey()操作用于将二项式堆中的节点的关键字值减小。这个操作在Dijkstra算法等最短路径算法中经常使用。具体实现方法如下:

  1. 首先找到要修改关键字值的节点,并将其“标记”为已修改。这意味着我们需要将节点的mark属性设置为True

  2. 如果节点成为了其他节点的子节点,则需要将其从父节点中移除,并将其重新插入堆中,以便保持堆的性质。

  3. 接着,我们需要递归向上遍历这个节点的父节点,将它们也“标记”为已修改。如果它们已经被“标记”了,我们需要将它们从父节点中移除,并将它们重新插入堆中。

  4. 最后,我们需要找到堆中的最小值,并将其返回。这可以通过遍历每个根节点,并选择具有最小关键字值的节点来完成。

以下是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算法等最短路径问题的能力。

在下一篇文章中,我们将演示如何使用二项式堆进行最短路径问题的求解。