Heaps are advanced data structures and are mostly implemented using priority queues. They can be thought of as a tree-based structure, in which the tree is a complete binary tree.
( A complete binary tree is a tree in which every other node other than the leaves have two children ) The 2 main characteristics of Heaps are
- It should be a complete binary tree.
- Nodes must be according to the Heap order property.
The 2 heap order priorities are
Max heap property - In a max-heap, the value of the root node should be the greatest among all the value of all of its children. The same property should be recursively true for all the sub-trees.
Min heap property - In a min-heap, the value of the root node should be the smallest among all the values of all of its children. The same property should be recursively true for all the sub-trees.
Applications of Heaps
- Heap Sort uses Binary Heap to sort an array in
O(N log N)
time. - Heaps are also used to implement Priority Queues.
- Priority queues, in turn, are used to solve graph algorithms and array related questions.
Representation of a Binary Heap
A binary heap is generally represented using an array, by level order traversal. The root element is always the first element of the array.
or a node, present at index i -
- Its
parent
can be found at -heap[(i-1)/2]
- Its
left child node
can be found at -heap[(2*i)+1]
- Its
right child node
can be found at -heap[(2*i)+2]
Operations and Time Complexities of a Heap
heapify()
- heapify operation is called when the binary heap violates the heap property. heapify rearranges the heap in such a way, that it becomes valid again. This takesO(log N)
time.getMin()
orgetMax()
- Returning the root node value of a min/max heap inO(1)
time.extractMin()
orextractMax()
- Removing the root node of a min/max heap. This takesO(log N)
time because heapify operation is called, to maintain the heap property, after removing the root node.insert()
: Inserting a new key takesO(log N)
time. We add the new key at the end of the max/min-heap. If the key violates the heap property, we traverse up and fix the tree.decreaseKey()
: Decreases the value of a key in a heap. If the decreased value violates the heap property, we traverse up and fix the tree. This takesO(log N)
time.increaseKey()
: Increases the value of a key in a heap. If the increased value violates the heap property, we traverse up and fix the tree. This takesO(log N)
time.delete()
: Deleting a key also takesO(log N)
time.
For min-heap, replace the value to be deleted with -INFINITY. Doing so, the value at the root node will now become -INFINITY. Then call extractMin()
.
For max-heap, replace the value to be deleted with +INFINITY. Doing so, the value at the root node will now become +INFINITY. Then call extractMax()
.
Implementing a min-heap using array
First we define a class for the heap.
class MinHeap
{
int *mh; // pointer to the array of heap elements
int maximum_size; // maximum size of the array or maximum number of heap nodes
int current_size; // current size of the array or current number of heap nodes
}
Constructor to initialize the heap - we would only be needing the maximum_size of the array as a parameter
MinHeap::MinHeap(int size)
{
current_size = 0;
maximum_size = size;
mh = new int[size];
}
Functions to return the parent, left, and right child node.
int parent(int i) { return (i-1)/2; }
int left(int i) { return (2*i + 1); }
int right(int i) { return (2*i + 2); }
Now, to insert a new key -
- First insert the new key at the end.
- Traverse up to fix the violated min-heap property if any.
void MinHeap::insertKey(int k)
{
if (current_size == maximum_size)
{
cout << "Overflow : Cannot insert more nodes";
return;
}
// First insert the new key at the end
current_size++;
int i = current_size - 1;
mh[i] = k;
// Traverse up & fix the min heap property if it is violated
while (i != 0 && mh[parent(i)] > mh[i])
{
swap(&mh[i], &mh[parent(i)]);
i = parent(i);
}
}
Now to decrease key.
- Decrease the key
- Traverse up & fix the min-heap property if it is violated
void MinHeap::decreaseKey(int i, int new_val)
{
// Decrease the key
mh[i] = new_val;
// Traverse up & fix the min-heap property if it is violated
while (i != 0 && mh[parent(i)] > mh[i])
{
swap(&mh[i], &mh[parent(i)]);
i = parent(i);
}
}
Now, heapify
method - To heapify a subtree with the root at given index i. The method assumes that the subtrees are already heapified.
void MinHeap::MinHeapify(int i)
{
int l = left(i);
int r = right(i);
int smallest = i;
if (l < current_size && mh[l] < mh[i])
smallest = l;
if (r < current_size && mh[r] < mh[smallest])
smallest = r;
if (smallest != i)
{
swap(&mh[i], &mh[smallest]);
MinHeapify(smallest);
}
}
Now, to extract the minimum/root node.
- Remove the root node.
- Replace the root node value with the last node value.
- Heapify the tree from the root.
int MinHeap::extractMin()
{
if (current_size <= 0)
return INT_MAX;
if (current_size == 1)
{
current_size--;
return mh[0];
}
// Remove the root node.
int root = mh[0];
// Replace the root node value with the last node value.
mh[0] = mh[current_size-1];
current_size--;
// Heapify the tree from the root.
MinHeapify(0);
return root;
}
Now, to delete a key.
- Replace the value of the key with INT_MIN.
- Extract the min/root node.
void MinHeap::deleteKey(int i)
{
decreaseKey(i, INT_MIN);
extractMin();
}
You can find the entire code here.
Subscribe to the newsletter to read more blogs on data structures and algorithms!
References -