Notes on Heaps
(related reading: Main & Savitch, pp. 492-499)
Building a better priority queue
- Using linear data structures to represent priority queues: insert is
O(1), extract is O(n).
- We could use binary search trees to represent priority queues: insert,
extract, both O(h). For balanced binary trees this might be okay, but for
imbalanced trees, this is worse than before.
- Using heaps : insert and extract are both O(lg(n))
The heap-order property
The key of each node is greater than the keys of its descendants.
(9)
/ \
(7) (3)
/ \ \
(5) (6) (1)
/
(2)
Heaps
A heap is a tree with the heap-order property and such that:
- each node has at most a fixed number of children
- Each non-leaf node has all its childern excpet for at most one which
is the deepest, rightmost non-leaf node.
In other words, new nodes are added from left to right, from top to
bottom.
(not a heap) (heap)
(9) (9)
/ \ / \
(7) (3) (7) (3)
/ \ / \ /
(5) (1) (5) (2) (1)
/
(2)
A heap that is a binary tree is known as a binary-heap.
Complete binary trees
A binary tree is completely full if every non-leaf node has exactly two
children and all the leaves are at the same depth.
(8)
/ \
(4) (6)
/ \ / \
(3) (1) (5) (7)
A binary tree is complete if each non-leaf node has all its children
except for at most one which is the deepest, rightmost non-leaf node.
(8) (8)
/ \ / \
(4) (6) (4) (6)
/ \ / /
(3) (1) (5) (3)
Heap operations
Operations on heaps are just like those on priority queues:
- insert
- maximum
- extractMax
maximum
The heap-order property implies that the item with maximum key must be
stored at the root. So maximum can simply return the item at root.
insert
- Add a new node at next open position in the complete tree.
- Copy the item/key into that node.
- If new node's key is greater than its parent's, swap, and repeat
considering the parent node until heap-order property is reestablished.
extractMax
- Save item at root.
- Move contents of last (bottommost, rightmost) node to root.
- Swap that item/key with the child that has the greater key.
- Repeat previous step until heap-order property is
reestablished.
- Return the item originally at root.
Implementing heaps
Binary trees --- easy, except, how do we find
"next available space in the tree?" There are ways to do this in O(lg(n)),
but we can do it in O(1) using arrays.
Arrays --- not efficient for trees in general, but very
efficient for complete binary trees.
Suppose heap is an array representing a heap.
- heap[0] is the root of the heap.
- For node at position i in the array,
- left child is at position (2*i)+1
- right child is at position (2*i)+2
- parent is at position (i-1)/2
- a member variable count can keep track of how many items
are in the heap and indicate the position for next insertion.
An example of a priority queue using array-based binary heaps can be found
here. Below is part of the declaration of
such a priority queue:
template <class Item, class Key>
class PQueue {
public:
...
Item maximum() const;
void insert(const Item& it, const Key& priority);
Item extractMax();
...
private:
...
typedef IKPair- IKP;
static const size_t INITIAL_CAPACITY = 2;
IKP *heap; // dynamic array
size_t count;
size_t capacity; // size of current dynamic array
// Helper functions:
size_t parent(size_t index) const;
size_t left(size_t index) const;
size_t right(size_t index) const;
bool hasLeft(size_t index) const;
bool hasRight(size_t index) const;
void heapifyDown(size_t index);
void heapifyUp(size_t index);
};
Faster sorting
Both insert and extractMax, for a priority queue using heap, have
worst-case run-time complexity of O(lg(n)) since the tree is always
complete. This means that sorting with a heap-base priority queue can be
done in O(n(lg(n)) the fastest sorting algorithm we've seen yet. This sort
is known as heapsort.