批量加载最小 - 最大堆

时间:2012-07-25 08:59:35

标签: algorithm data-structures heap bulk bulk-load

最小 - 最大堆是一个堆,可以在O(1)中找到最小和最大元素,并在O(log n)中将其删除。它与经典堆密切相关,但它实际上交错了三堆:最小堆和两个最大堆,其中偶数级是最小级别,奇数级别是最大级别(因此有两个根)。经典的堆属性适用于孙子而不是孩子。 min-max-heap的叶级别基本上在最小和最大堆之间共享,此处插入的新元素可以移动到树的偶数或奇数级别。

虽然筛选和筛选是直接修改,但棘手的部分是当元素需要从最小有序部分移动到堆的最大有序部分时。

对于经典堆,我可以通过执行自下而上的堆修复来在O(n)中批量加载树,而明显的逐个插入需要O(n log n)时间。我也可以为批量插入执行此操作,而不是逐个插入它们,我可以将它们全部附加并批量修复堆。

对于min-max堆,我当然可以在O(n log n)中线性加载它,但我想知道是否还有一种方法可以在O(n)中批量加载它或批量修复堆自下而上?

2 个答案:

答案 0 :(得分:2)

我将用我迄今为止发现的内容回答自己,对于可能有同样问题的其他人:

最小 - 最大堆基本上是三个堆,具有共享叶级别。

       min1           <--- one min heap, first level
     /      \
   mi2       mi3      <--- third level
  /   \     /   \
 m5   m6   m7   m8    <--- fifth level
 /\   /\   /\   /\
a  b c  d e  f g  h   <--- leaf level (here: sixth level)
 \/   \/   \/   \/
 x1   x2   x3   x4    <--- fourth level
   \ /       \ /
   max1      max2     <--- two max heaps, second level

(重要说明:这不完全正确,因为堆有4个扇出!加上,这是逻辑顺序,而不是内存布局,它在水平方向上交错堆积) 叶级别的对象属于所有三个堆,这是元素从堆的最小部分转换到最大部分的位置。

现在可以计算最小堆和最大堆的大小,使用部分排序(如quickselect)来分区数据并分别批量加载这三个部分。但是, quickselect已经与您希望整个批量加载(部分订购数据集)一样昂贵! 批量加载和批量修复(!)堆的另一种显而易见的方法是查看较小的子堆。在常规的min-heap中,你会看到三个元素a,b,c的原子堆,并确保a是最小的。在这里,我们可以看到高度为4的堆,即15个元素:

         min1
         /  \
    max1      max2
   /  \        /  \
 m1    m2    m3    m4
 /\    /\    /\    /\
a  b  c  d  e  f  g  h

确保min1是最小的,max1和max2是最大的两个,m1-m4是接下来的4个最大的,并且以两个级别的步长爬上树(即仅限最小级别)

或者我们可以查看大小为7(3个级别)的堆并识别最小和最大类型

   min1           max1
   /  \           /  \
max1  max2     min1  min2
 /\    /\       /\    /\
a  b  c  d     a  b  c  d

确保对于最低级别我们有第一种类型,对于最高级别我们有第二种类型。然后我们需要经历所有级别。

但也许更好的解决方案是选择间隔堆。这也基本上是交错的最小和最大堆。但是,它们是对称交错的并且具有相同的大小。它们似乎更容易实现,并且可以解释为看起来像这样的堆:

      min1,max1
      /       \
min2,max2   min3,max3

有关批量加载的详细信息,请参阅原始间隔堆发布。

因此,如果您对可加载大容量的min-max-heap感兴趣,请考虑查看间隔堆!有人说他们的表现优于min-max-sheaps;它们密切相关,应该更容易实施。特别是,没有明显的理由说明最小 - 最大堆应该表现得更好,如果详细的复杂性分析表明它们在比较和交换中需要一个常数因素而表现更差,我也不会感到惊讶(因为就我而言)可以天真地说,min-max-heap需要更多的比较来验证堆的正确性。)

答案 1 :(得分:0)

我认为自下而上的树修复应该有效:

def heapify(N)
  if (N is a min-node)
     if(*N > *left_child(N))
        swap(*N, *left_child(N))
     if(*N > right_child(N))
        swap(*N, *right_child(N))
     find the smallest X among N, grand-children(N)
  else
     if(*N < left_child(N))
        swap(*N, *left_child(N))
     if(*N < right_child(N))
        swap(*N, *right_child(N))
     find the largest X among N, grand-children(N)
  if(X != N)
     swap(*X, *N)
     heapify(X)

load heap in arbitrary order
for each N in bottom-up order of heap
   heapify(N)