代码之家  ›  专栏  ›  技术社区  ›  Has QUIT--Anony-Mousse

批量加载最小-最大堆

  •  2
  • Has QUIT--Anony-Mousse  · 技术社区  · 12 年前

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

    虽然向上筛选和向下筛选是简单的修改,但棘手的部分是元素需要从堆的最小排序部分移动到最大排序部分。

    对于经典堆,我可以在 O(n) 通过执行自下而上的堆修复,而显而易见的逐个插入需要 O(n log n) 时间我也可以对批量插入执行此操作,而不是一个接一个地插入它们,我可以将它们全部追加,然后批量修复堆。

    对于最小-最大堆,我当然可以线性加载它 O(n对数n) ,但我想知道是否也有散装的方法 O(n) 还是自下而上批量修复堆?

    2 回复  |  直到 11 年前
        1
  •  2
  •   Has QUIT--Anony-Mousse    12 年前

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

    最小-最大堆本质上是 堆在一起,具有共享的叶级别。

           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已经和你想要的整个批量装载一样昂贵了 (部分订购数据集)! 批量加载和批量修复(!)堆的另一种明显方法是查看较小的子堆。在一个常规的最小堆中,您可以查看三个元素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
    

    有关大容量加载的详细信息可以在原始间隔堆发布中找到。

    因此,如果您对批量可加载的最小-最大堆感兴趣,请考虑查看间隔堆! 有些人说,无论如何,它们的表现都优于最小-最大堆;它们密切相关,应该更容易实现。特别是,没有明显的理由说明最小-最大堆应该表现得更好,如果详细的复杂性分析显示,在所需的比较和交换中,它们的表现更差是一个恒定的因素,我也不会感到惊讶(因为据我所知,最小-最大堆栈需要更多的比较来验证堆的正确性)。

        2
  •  0
  •   maniek    12 年前

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

    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)