📜  二项堆(1)

📅  最后修改于: 2023-12-03 15:36:04.193000             🧑  作者: Mango

二项堆(Binomial Heap)

简介

二项堆是一种数据结构,可以高效地实现插入和合并操作,还可以在对堆进行删除最小元素操作时达到对数级别的复杂度。与其他堆相比,它具有更好的合并性质,使得合并操作的时间复杂度更低,同时可以维护一些其他有用的信息,如堆的大小等。

结构

二项堆结构由多个二项树(Binomial Tree)组成,每个二项树是一个递归定义的数据结构,它由一个根节点、它的左子树和从它的左子树分离出来的一些二项树组成。一个二项树的左子树的形态是由多个二项树构成的,其中每个二项树的大小为 $2^{k}$,其中 $k \in \mathbb{Z}$,并且每个二项树的根节点的度数递增。以下是一些例子:

Examples of Binomial Trees, from Wikipedia

在二项堆中,每个二项树的大小是唯一的,且堆中最多只有一棵大小为 $k$ 的二项树。关于二项树的更多信息,可以参考二项树的相关文章。

操作
插入

插入操作很简单,只需要利用二项树自带的插入操作,将新元素封装成大小为 $0$ 的二项树,然后将这棵二项树与堆中已存在的其他二项树合并即可。

合并

合并两个二项堆需要找到它们之间大小相同的二项树进行合并。具体步骤如下:

  1. 将两个堆中的二项树按照大小从小到大排序,将它们连接成一个环形链表。
  2. 从环形链表的顶端开始,将大小相同的二项树合并。为了方便合并,保留一个指向最小二项树的指针。
  3. 当链表中只剩下一棵二项树时,合并操作结束。

合并操作的复杂度为 $O(\log n)$,其中 $n$ 是堆的大小。

删除最小元素

删除最小元素操作也很简单,只需要找到最小元素所在的二项树,并将它从二项堆中删除。为了使得剩余的二项树构成一个新的二项堆,需要将最小二项树的每个子树都分离出来,用来与其他二项树合并。具体步骤如下:

  1. 找到最小元素所在的二项树,并保留它的子树列表。
  2. 从二项堆中删除最小二项树,并将它的子树列表中的每个二项树也从堆中删除。这些二项树将会被用来合并其余的二项树。
  3. 将剩余的二项树按照大小从小到大排序,然后按照合并步骤中的方式进行合并。
  4. 合并结束后,得到了一个新的二项堆。

删除最小元素操作的复杂度为 $O(\log n)$。

实现

二项堆可以使用链表的形式进行实现,每个节点代表一个二项树。节点有三个主要属性:父节点指针,左兄弟节点指针和右兄弟节点指针。其中,右兄弟指针用于链接兄弟节点,左兄弟指针和父节点指针用于保证二项树的结构性。以下是一个二项堆链表的实现代码片段(参考Python实现):

class BinomialTree:
    def __init__(self, val):
        self.val = val
        self.degree = 0
        self.parent = None
        self.left_child = None
        self.right_sibling = None

class BinomialHeap:
    def __init__(self):
        self.head = None
        self.size = 0

    def merge(self, other: "BinomialHeap") -> "BinomialHeap":
        """合并两个二项堆"""
        # 省略合并代码

    def insert(self, val):
        """插入一个新元素"""
        new_heap = BinomialHeap()
        new_heap.head = BinomialTree(val)
        self.merge(new_heap)

    def extract_min(self):
        """删除并返回最小元素"""
        # 省略删除最小元素代码
总结

二项堆是一种十分高效的堆数据结构,它的插入、合并和删除最小元素操作都能够以对数级别的时间复杂度完成。相较于其他形态的堆,它的合并操作有更好的性质,能够更快地完成。通过链表的方式,二项堆可以方便地实现。