简体   繁体   中英

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. How it is producing output: 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

I would really appreciate if anyone can help me in understanding how make_heap() generated such a output.

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

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. In general, children of node n are at positions 2*n + 1 and 2*n + 2 .

In C++, the make_heap function together with push_heap and pop_heap implement a complete priority queue over vector. There is alsopriority_queue container wrapper that combines them in a class.

Priority queues are primarily used in the famous Dijkstra's algorithm and various scheduling algorithms. Because Dijkstra's algorithm needs to select minimum, it is more common to define heap with minimum in the root. 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.


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 ]
    • 24 < 91
  2. [13, 67, 32 , 91, 59, 41 , 23 , 24]
    • 32 < 41
  3. [13, 67 , 41, 91 , 59 , 32, 23, 24]
    • 67 < 91
  4. [ 13 , 91 , 41 , 67, 59, 32, 23, 24]
    • 13 < 91
  5. [91, 13 , 41, 67 , 59 , 32, 23, 24]
    • the moved down element might still be violating constraint and this time it does: 13 < 67
  6. [91, 67, 41, 13 , 59, 32, 23, 24 ]
    • and still violating the constraint: 13 < 24
  7. [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. This will result in the heap that you expected. That algorithm results in n heapify operations. The algorithm used by make_heap() results in n/2 heapify operations. It results in a different but still valid heap.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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