Section 4 - Order Heap and the Array Constructor

7A.4.1 The Vector Constructor

scenic picWe will discuss the interesting constructor that takes a vector (unordered) as a parameter. The general idea is that we first use the size of the vector as a guide to create the proper size heap. Once we have created the heap, we copy the array into the internal mArray, shifting everything right one, to avoid location 0. So far, pretty easy.

The only thing is this: we have a complete mess from the standpoint of ordering. The tree is filled out properly, but the heap order is correct practically nowhere. We will create a separate function, orderHeap() whose job it is to take a randomly ordered tree that has heap structure (but not heap order) and makes its order correct. We don't actually pass a heap as a parameter to orderHeap() since it will be a private instance method, and therefore use the this object as its heap.

Assuming, then, that orderHeap() is available for us to use (it will be soon), here is the constructor:

template <class Comparable>
FHbinHeap<Comparable>::FHbinHeap( const FHvector<Comparable> & items )
   : mSize(items.size())
{
   int k;

   // find the first perfect tree size > items.size() and INIT_CAPACITY
   for (mCapacity = INIT_CAPACITY; 
      mCapacity <= mSize;
      mCapacity = 2 * mCapacity
      )
   {
      if (mCapacity < 0)
         mCapacity = mSize + 1; // give up - overflow, and not perfect
   }
   mArray.resize(mCapacity);

   // copy starting with position 1 - no ordering yet
   for(k = 0; k < mSize; k++ )
      mArray[k+1] = items[k];

   // order the heap
   orderHeap();
}

The sizing of the internal array is identical in function to that of the default constructor. Everything else is as I said, so we can dispense with further discussion and cut to the chase. How do we write orderHeap()?

7A.4.2 orderHeap()

First look:

template <class Comparable>
void FHbinHeap<Comparable>::orderHeap()
{
   int k;

   for(k = mSize/2; k > 0; k-- )
      percolateDown(k);
}

Holy Smokes. It's so simple. But why does it work?

This is a very elegant and subtle algorithm. It works on the following premise:

percolateDown() Requirement

percolateDown() will do the right thing at a given tree node if the two sub-trees below it are both heap-ordered.

That's how we designed and used it. We took a somewhat random value from the bottom of the tree and dropped it into the root. The tree was ordered already below that root, and percolateDown() simply adjusted the tree to accommodate the node at the root, finding its correct place and making a few adjustments along the way.

Now, however, we have a mess of a tree. The key is, to start with the second-from-bottom level of the tree and perform percolateDown() operations on each node. These nodes are roots of trees that have, at most, three nodes total: the node itself and zero, one or two children. Since individual child nodes vacuously satisfy heap order, nodes in this second-from-bottom layer are perfect candidates for using percolateDown(). Furthermore, after we have applied percolateDown() to every node in the second lowest layer, all these nodes are then heap ordered, so we can move up to the next higher layer and do the same thing. When done with that layer, we move up again, and repeat. We are ordering the heap from bottom to top, one node-at-a-time.

A little thought will reveal that mSize/2 is the correct node to start the process, and we move left and up, one-node-at-a-time until we get to the root.

This is an expensive operation, but we only do it once when we have to create a binary min-heap from a vector.

Why, by the way, do we even anticipate this type of constructor? When will we use it? It plays a prominent role when we get to heap sort, a very fast and elegant sort that makes use of the characteristics of heaps to sort a random array.

7A.4.3 A Sample Client

Finally, we demonstrate a binary heap using a simple client.

// CS_2C  Binary Heap Client
#include <iostream>
#include <string>
#include "FHbinHeap.h"
using namespace std;

int main()
{
   // first set of tests --------------------
    FHbinHeap<string> heap;
   string s1 = "nureyev", s2 = "ailey", s3 = "nijinsky";

   heap.insert(s1);
   heap.insert(s1);
   heap.insert(s2);
   heap.insert(s2);
   heap.insert(s3);
   heap.insert(s3);

   try
   {
      while (true)
         cout << "removed " << heap.remove() << endl;
   }
   catch (FHbinHeap<string>::HeapEmptyException)
   {
      cout << "heap empty\n\n";
   }

   // second set of tests --------------------

   FHbinHeap<string> heap2;
   string substrate = 
      "asdlkfj asdoiBIUYVuwer slkdjLJfwoe89)B)(798rjMG0293lkJLJ42lk3j)(*";
   string str_array[500];
   unsigned int k, limit;

   substrate = substrate + substrate;

   for (k = 0; k < substrate.length() - 6; k++)
      str_array[k] = substrate.substr(k, 5);
   limit = k;

   for (k = 0; k < limit; k++)
      heap2.insert(str_array[k]);
   cout << "\n#strings generated: " << limit 
      << "\n#elements in heap: " << heap2.size() << endl;

   try
   {
      while (true)
         cout << "removed " << heap2.remove() << endl;
   }
   catch (FHbinHeap<string>::HeapEmptyException)
   {
      cout << "heap empty\n\n";
   }

   return 0;
}

Here is the output. Study the above client and following output to make sure you understand how the heap is used.

removed ailey
removed ailey
removed nijinsky
removed nijinsky
removed nureyev
removed nureyev
heap empty


#strings generated: 124
#elements in heap: 124
removed  asdo
removed  asdo
removed  slkd
removed  slkd
removed (*asd
removed (798r
removed (798r
removed )(*as
removed )(798
removed )(798
removed )B)(7
removed )B)(7
removed *asdl
removed 0293l
removed 0293l
removed 293lk
removed 293lk
removed 2lk3j
removed 2lk3j
removed 3j)(*
removed 3lkJL
removed 3lkJL
removed 42lk3
removed 42lk3
removed 798rj
removed 798rj
removed 89)B)
removed 89)B)
removed 8rjMG
removed 8rjMG
removed 9)B)(
removed 9)B)(
removed 93lkJ
removed 93lkJ
removed 98rjM
removed 98rjM
removed B)(79
removed B)(79
removed BIUYV
removed BIUYV
removed G0293
removed G0293
removed IUYVu
removed IUYVu
removed J42lk
removed J42lk
removed JLJ42
removed JLJ42
removed Jfwoe
removed Jfwoe
removed LJ42l
removed LJ42l
removed LJfwo
removed LJfwo
removed MG029
removed MG029
removed UYVuw
removed UYVuw
removed Vuwer
removed Vuwer
removed YVuwe
removed YVuwe
removed asdlk
removed asdlk
removed asdoi
removed asdoi
removed djLJf
removed djLJf
removed dlkfj
removed dlkfj
removed doiBI
removed doiBI
removed e89)B
removed e89)B
removed er sl
removed er sl
removed fj as
removed fj as
removed fwoe8
removed fwoe8
removed iBIUY
removed iBIUY
removed j asd
removed j asd
removed j)(*a
removed jLJfw
removed jLJfw
removed jMG02
removed jMG02
removed k3j)(
removed kJLJ4
removed kJLJ4
removed kdjLJ
removed kdjLJ
removed kfj a
removed kfj a
removed lk3j)
removed lk3j)
removed lkJLJ
removed lkJLJ
removed lkdjL
removed lkdjL
removed lkfj
removed lkfj
removed oe89)
removed oe89)
removed oiBIU
removed oiBIU
removed r slk
removed r slk
removed rjMG0
removed rjMG0
removed sdlkf
removed sdlkf
removed sdoiB
removed sdoiB
removed slkdj
removed slkdj
removed uwer
removed uwer
removed wer s
removed wer s
removed woe89
removed woe89
heap empty

Press any key to continue . . .