简体   繁体   English

make_heap() 函数如何工作?

[英]How make_heap() function works?

I have basic understanding of Vectors and iterators.我对向量和迭代器有基本的了解。 But I am facing problem in understanding the output of the below code snippet.但是我在理解以下代码片段的输出时遇到了问题。

To be specific, I am unable to findout the functionality of make_heap() function.具体来说,我无法找到 make_heap() 函数的功能。 How it is producing output: 91 67 41 24 59 32 23 13 !!它是如何产生输出的:91 67 41 24 59 32 23 13 !!

As per my knowledge, the heap will look like this:据我所知,堆看起来像这样:

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

So, I was expecting the output as: 91 67 41 59 24 32 23 13所以,我期待输出为:91 67 41 59 24 32 23 13

I would really appreciate if anyone can help me in understanding how make_heap() generated such a output.如果有人能帮助我理解 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;
}   

A binary heap must satisfy two constraints (in addition for being a binary tree):二叉堆必须满足两个约束(除了是二叉树):

  1. The shape property - the tree is a complete binary tree (except for the last level) shape 属性——树是完全二叉树(最后一层除外)
  2. The heap property: each node is greater than or equal to each of its children堆属性:每个节点都大于或等于它的每个子节点

The ordering of siblings in a binary heap is not specified by the heap property, and a single node's two children can be freely interchanged unless doing so violates the shape property.二叉堆中兄弟节点的顺序不是由堆属性指定的,单个节点的两个子节点可以自由互换,除非这样做违反了形状属性。

So in your example you can freely interchange between the nodes in the second level and get multiple outputs which are all legal.因此,在您的示例中,您可以在第二级节点之间自由交换并获得多个均合法的输出。

The make_heap constructs a Binary Heap in the vector by reordering the elements so that they satisfy the heap constraint. make_heap通过对元素重新排序以使其满足堆约束,从而在向量中构造一个二叉堆 The heap constructed is a Maximal Heap , that is it puts the largest (according to operator< or provided compare) element in first element, the root of the heap, which is first element of the vector.构造的堆是一个最大堆,也就是说,它将最大的(根据operator<或提供的比较)元素放在第一个元素中,堆的根,它是向量的第一个元素。

Binary heap is a balanced binary tree satisfying the condition that value in parent node is always larger (in this case, smaller is more common) than values of the child nodes.二叉堆是一棵平衡二叉树,它满足父节点的值总是大于子节点的值(在这种情况下,更小更常见)的条件。 That means the root always contains largest element.这意味着根总是包含最大的元素。 Combined with efficient extraction of the root, this makes a good priority queue.结合根的有效提取,这构成了一个很好的优先级队列。

A binary heap is stored in array in breadth-first preorder.二叉堆以广度优先的预序存储在数组中。 That is root is at position 0, it's immediate children at positions 1 and 2, children of 1 at positions 3 and 4, children of 2 at positions 5 and 6 and so on.即根在位置 0,它是位置 1 和 2 的直接子代,位置 3 和 4 的 1 的子代,位置 5 和 6 的 2 的子代,依此类推。 In general, children of node n are at positions 2*n + 1 and 2*n + 2 .通常,节点n子节点位于2*n + 12*n + 2

In C++, the make_heap function together with push_heap and pop_heap implement a complete priority queue over vector.在 C++ 中, make_heap函数与push_heappop_heap一起实现了一个完整的优先级队列超过向量。 There is alsopriority_queue container wrapper that combines them in a class.还有priority_queue容器包装器将它们组合在一个类中。

Priority queues are primarily used in the famous Dijkstra's algorithm and various scheduling algorithms.优先队列主要用于著名的 Dijkstra 算法和各种调度算法。 Because Dijkstra's algorithm needs to select minimum, it is more common to define heap with minimum in the root.由于 Dijkstra 算法需要选择最小值,因此更常见的是在根中定义具有最小值的堆。 C++ standard library chose to define it with maximum, but note that you can trivially get minimal heap by giving it greater_than instead of less_than as comparator. C ++标准库选择具有最大定义它,但请注意,您可以通过平凡赋予它得到最小堆greater_than代替less_than作为比较。


There are two ways to build a heap.有两种方法可以构建堆。 By pushing each element to it, or by fixing the invariant from the first half of elements (the second half are leafs).通过将每个元素推向它,或者通过从元素的前半部分(后半部分是叶子)固定不变量。 The later is more efficient, so:后者效率更高,所以:

  1. [13, 67, 32, 24 , 59, 41, 23, 91 ] [13, 67, 32, 24 , 59, 41, 23, 91 ]
    • 24 < 91 24 < 91
  2. [13, 67, 32 , 91, 59, 41 , 23 , 24] [13, 67, 32 , 91, 59, 41 , 23 , 24]
    • 32 < 41 32 < 41
  3. [13, 67 , 41, 91 , 59 , 32, 23, 24] [13, 67 , 41, 91 , 59 , 32, 23, 24]
    • 67 < 91 67 < 91
  4. [ 13 , 91 , 41 , 67, 59, 32, 23, 24] [ 13 , 91 , 41 , 67, 59, 32, 23, 24]
    • 13 < 91 13 < 91
  5. [91, 13 , 41, 67 , 59 , 32, 23, 24] [91, 13 , 41, 67 , 59 , 32, 23, 24]
    • the moved down element might still be violating constraint and this time it does: 13 < 67向下移动的元素可能仍然违反约束,这次确实是:13 < 67
  6. [91, 67, 41, 13 , 59, 32, 23, 24 ] [91, 67, 41, 13 , 59, 32, 23, 24 ]
    • and still violating the constraint: 13 < 24并且仍然违反约束:13 < 24
  7. [91, 67, 41, 24, 59, 32, 23, 13] [91, 67, 41, 24, 59, 32, 23, 13]
    • root processed, we are done根处理,我们完成了

When heapifying an unsorted array, the algorithm take advantage of the face that half the array will be leaf nodes (the higher indexes in the array) and the other half will be parents to those leaf nodes.当堆化一个未排序的数组时,该算法利用这样的一面,即一半数组将是叶节点(数组中的较高索引),另一半将是这些叶节点的父节点。 The algorithm only has to iterate over the parent nodes and fix up their logical sub-trees.该算法只需要遍历父节点并修复它们的逻辑子树。 The leaf nodes start out as valid sub-heaps since by definition they are greater than their non-existent child nodes.叶节点作为有效的子堆开始,因为根据定义,它们大于它们不存在的子节点。

So we only have to fix up the sub-heaps that have at least one non-leaf node.所以我们只需要修复至少有一个非叶子节点的子堆。 Done in the correct order (from the middle of the array to the lowest index), when the last parent node is heapified, the whole array will be a valid heap.以正确的顺序(从数组的中间到最低索引)完成,当最后一个父节点被堆化时,整个数组将是一个有效的堆。

Each step looks as follows:每个步骤如下所示:

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
             --          --

The naive method of heapifying an array is to walk through the array from index 0 to n-1 and at each iteration 'add' the element at that index to a heap made up of the elements before that index.堆化数组的简单方法是遍历数组从索引0n-1并在每次迭代时将该索引处的元素“添加”到由该索引之前的元素组成的堆中。 This will result in the heap that you expected.这将导致您期望的堆。 That algorithm results in n heapify operations.该算法导致n堆化操作。 The algorithm used by make_heap() results in n/2 heapify operations. make_heap()使用的算法导致n/2堆化操作。 It results in a different but still valid heap.它导致不同但仍然有效的堆。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM