A heap is a way to implement a priority queue.
The property of a priority queue is:
The highest priority item could be:
For these notes the bigger item has a higher priority.
What are some uses for priority queues:
Why not use an ADT we already know to implement priority queues?
Heaps have same complexity as a balanced search tree but:
A heap is a binary tree that has special structure compared to a general binary tree:
This is easier because we only require property 1 and not the BST properties.
Complete binary tree: parents of all but leaf nodes have 2 children. Leaves can differ in depth by 1 but the smaller depth leaves must be on the right part of the binary tree. The nodes on the bottom level fill from left to right.
The example from BST as a heap could be:
As with a BST, the location in the heap is not unique. This is a heap with the same values:
This isn't a heap since it is not complete
If the binary tree is to be complete, some item must wind up in the next location. To begin we place the new item there. The problem is it may be in the wrong place so we have to fix this. Overall you get:
Step 2. lets the item percolate up to its correct location.
Let's insert 14 into our first example:
Since the height is log(n), the complexity is worst case O(log(n)). It has been shown that on average you only shift the inserted value 1.6 locations up. Thus, it is O(1) in practice.
Finding the item to remove is easy - it is the root of the tree.
Removing this item destroys the heap structure.
As with insert, we begin by maintaining the complete binary tree structure. Next we fix up the ordering rule for a heap.
The steps are:
Step 3. lets the item percolate down to its correct location.
Continuing our example:
Since the height is log(n), the complexity is O(log(n)). Since you take a value from the bottom, which is small, you generally have to do all log(n) steps.
When we talked about general trees, we saw two ways to store them:
We needed this generality because the number of children varied.
For a complete binary tree we can easily use an array since you must have 2 children of every node except for leaves and these must at the bottom of the tree.
The storage scheme is simple: you put them in the array starting at index 0 as they occur in a level-order traversal.
Our previous example is:
A good question is how do you know what size array to use?
If you know the number of nodes then create an array of that size.
If you don't know the exact number you will have but have an idea of the maximum height you can use:
If the tree grows beyond this you can resize it.
How can you get the tree structure from the array location?
You can tell if the child does not exist by:
To do the inverse operation:
In our example:
The root that has the maximum priority is at index 0.
Why not use this for a general binary tree?
You can have "missing children" in a general binary tree. These represent empty locations (null references) in the array. Using a linked representation can cut down on this (you only need the first null child). Which is better depends on how full the binary tree is.