make_heap()函数如何工作?

时间:2013-06-27 07:23:31

标签: c++ vector stl

我对Vectors和迭代器有基本的了解。但是我在理解下面代码片段的输出方面遇到了问题。

具体来说,我无法找到make_heap()函数的功能。 它是如何产出的:91 67 41 24 59 32 23 13 !!

据我所知,堆看起来像这样:

        91
       /  \
     67    41
    /  \  /  \
   59 24 32  23
  /
 13

所以,我期待输出为: 91 67 41 59 24 32 23 13

如果有人能帮助我理解make_heap()如何产生这样的输出,我真的很感激。

int main()
{
int aI[] = { 13, 67, 32, 24, 59, 41, 23, 91 };
vector<int> v(aI, aI + 8);

make_heap( v.begin(), v.end() );

vector<int>::iterator it;
for( it = v.begin(); it != v.end(); ++it )
    cout << *it << " ";
//Output: 91  67  41  24  59  32  23  13

    return 0;
}   

3 个答案:

答案 0 :(得分:6)

二进制堆必须满足两个约束(另外还有二叉树):

  1. 形状属性 - 树是完整的二叉树(最后一级除外)
  2. 堆属性:每个节点大于或等于其子节点
  3. 堆属性不指定二进制堆中兄弟节点的顺序,并且单个节点的两个子节点可以自由互换,除非这样做违反了shape属性。

    因此,在您的示例中,您可以在第二级节点之间自由交换,并获得多个合法的输出。

答案 1 :(得分:3)

当堆积未排序的数组时,该算法利用了一半的数组将是叶子节点(数组中的较高索引),而另一半将是父节点到那些叶子节点。该算法只需迭代父节点并修复其逻辑子树。叶节点从有效子堆开始,因为根据定义它们比它们不存在的子节点大。

所以我们只需要修复至少有一个非叶子节点的子堆。以正确的顺序(从数组的中间到最低的索引)完成,当最后一个父节点进行堆化时,整个数组将是一个有效的堆。

每个步骤如下:

iteration 1:

    13 67 32 24 59 41 23 91
              ^              current parent under consideration
                          ^  children of this parent

    13 67 32 91 59 41 23 24  after heapifying this sub-tree
             --          --


iteration 2:

    13 67 32 91 59 41 23 24
           ^                 current parent under consideration
                    ^  ^     children of this parent

    13 67 41 91 59 32 23 24  after heapifying this sub-tree
          --       -- 


iteration 3:

    13 67 41 91 59 32 23 24
        ^                    current parent under consideration
              ^  ^           children of this parent

    13 91 41 67 59 32 23 24  after heapifying this sub-tree
       --    --

iteration 4:

    13 91 41 67 59 32 23 24
     ^                       current parent under consideration
        ^  ^                 children of this parent

    91 13 41 67 59 32 23 24  heapify swap 1
    -- --
    91 67 41 13 59 32 23 24  heapify swap 2
       --    --
    91 67 41 24 59 32 23 13  after heapifying this sub-tree
             --          --

堆积数组的简单方法是从索引0遍历数组到n-1,并在每次迭代时将该索引处的元素“添加”到由元素组成的堆中那个指数。这将导致您期望的堆。该算法导致n堆化操作。 make_heap()使用的算法导致n/2堆化操作。它会产生一个不同但仍然有效的堆。

答案 2 :(得分:2)

make_heap通过重新排序元素来构造向量中的Binary Heap,以便它们满足堆约束。构造的堆是 Maximal Heap ,即它将第一个元素中的最大(根据operator<或提供的compare)元素放入堆中,这是向量的第一个元素。

二进制堆是一个平衡二叉树,它满足父节点中的值总是比子节点的值更大(在这种情况下,更小的更常见)的条件。这意味着根始终包含最大元素。结合有效的根提取,这是一个很好的优先级队列。

二进制堆以广度优先顺序存储在数组中。这是根位置0,它位于第1和第2位的直接孩子,第3和第4位的1岁儿童,第5和第6位的2岁儿童,依此类推。通常,节点n的子节点位于2*n + 12*n + 2

在C ++中,make_heap函数与push_heappop_heap一起在向量上实现完整的优先级队列。还有priority_queue容器包装器将它们组合在一个类中。

优先级队列主要用于着名的Dijkstra算法和各种调度算法。因为Dijkstra的算法需要选择最小值,所以更常见的是在根中定义具有最小值的堆。 C ++标准库选择使用maximum来定义它,但请注意,通过将greater_than而不是less_than作为比较器,您可以轻松地获得最小堆。


有两种方法可以构建堆。通过将每个元素推向它,或者通过固定前半部分元素的不变量(后半部分是叶子)。后者效率更高,所以:

  1. [13,67,32, 24 ,59,41,23, 91 ]
    • 24&lt; 91
  2. [67,13, 32 ,91,59, 41 23 ,24]
    • 32&lt; 41
  3. [67, 13 ,41, 91 59 ,32,23,24]
    • 13&lt; 91
  4. [67,91,41, 13 ,59,32,23, 24 ]
    • 移动的元素可能仍然违反约束,这次它确实违反约束:13&lt; 24
  5. [ 67 91 41 ,24,59,32,23,13]
    • 67&lt; 91
  6. [91,67,41,24,59,32,23,13]
    • !(67&lt; 24),根处理,我们完成了