我正在阅读Pairing heap。
这很简单,唯一棘手的部分是delete_min
操作。
唯一重要的基本操作是从堆中删除最小元素。标准策略首先从左到右成对合并子堆(这是给这个数据结构命名的步骤),然后从右到左合并生成的堆列表:
我认为我不需要在此处复制/粘贴代码,因为它位于 wiki 链接中。
我的问题是
为什么他们做这两个通过合并?
为什么他们首先合并对?不直接合并它们吗?
还有为什么在合并对之后,专门从右到左合并?
我正在阅读Pairing heap。
这很简单,唯一棘手的部分是delete_min
操作。
唯一重要的基本操作是从堆中删除最小元素。标准策略首先从左到右成对合并子堆(这是给这个数据结构命名的步骤),然后从右到左合并生成的堆列表:
我认为我不需要在此处复制/粘贴代码,因为它位于 wiki 链接中。
我的问题是
为什么他们做这两个通过合并?
为什么他们首先合并对?不直接合并它们吗?
还有为什么在合并对之后,专门从右到左合并?
使用配对堆,向堆中添加项目是一个 O(1) 操作,因为它所做的只是将节点添加为新根(如果它小于当前根),或者作为当前根的第一个子节点。因此,如果您创建了一个配对堆并按顺序将数字 0 到 9 添加到其中,您最终会得到:
0
|
-----------------
| | | | | | | | |
9 8 7 6 5 4 3 2 1
如果您随后执行delete-min,则必须查看每个子项以确定最小项并构建新堆。如果你使用从左到右的简单组合方法,你最终会得到这棵树:
1
|
---------------
| | | | | | | |
9 8 7 6 5 4 3 2
下次您执行delete-min时,您必须查看剩余的 8 个子项等。使用此技术,创建然后从堆中删除所有项将是 O(n^2) 操作。
成对组合然后组合成对的两遍方法导致更有效的结构。考虑第一种情况。删除最小项目后,我们剩下九个孩子。它们从左到右成对组合产生:
8 6 4 2 1
/ / / /
9 7 5 3
然后我们从右到左组合这些对。在步骤:
8 6 4 1
/ / / /
9 7 5 2
/
3
8 6 1
/ / / \
9 7 2 4
/ /
3 5
8 1
/ |
9 ---------
6 4 2
/ / /
7 5 3
1
|
----------
8 6 4 2
/ / / /
9 7 5 3
现在,下次我们调用delete-min时,只有四个节点要检查,而下一次之后将只有两个。使用两次合并方法将子级别的节点数量减少了至少一半。我展示的安排是最坏的情况。如果项目按升序排列,则第一个delete-min操作将生成一棵树,其根下方只有两个子节点。
这是配对堆摊销复杂性的一个特别好的例子。insert是 O(1),但是在一堆插入操作之后的第一个delete-min是 O(n),其中是自上次delete-min以来插入的项目数。两遍合并规则的美妙之处在于它可以快速重组堆以降低 O(n) 复杂度。n
使用此组合规则, delete-min的摊销复杂度为 O(log n)。使用严格的从左到右规则,它是 O(n)。