How to Implement and Use Heaps in C
Table of Contents
- Fundamental Concepts of Heaps
- Implementation of a Binary Heap in C
- Usage Methods and Common Practices
- Best Practices
- Conclusion
- References
Fundamental Concepts of Heaps
A heap is a complete binary tree that satisfies the heap property. There are two types of heaps:
- Max Heap: In a max heap, for every node
iother than the root, the value of the node is less than or equal to the value of its parent. That is,arr[parent(i)] >= arr[i]for alli. - Min Heap: In a min heap, for every node
iother than the root, the value of the node is greater than or equal to the value of its parent. That is,arr[parent(i)] <= arr[i]for alli.
The following are some important properties of a heap:
- Complete Binary Tree: A heap is a complete binary tree, which means that all levels of the tree are completely filled except possibly the last level, and the nodes in the last level are filled from left to right.
- Efficient Access: The root of the max heap contains the maximum element, and the root of the min heap contains the minimum element. Insertion and deletion operations in a heap can be done in $O(log n)$ time complexity, where $n$ is the number of elements in the heap.
Implementation of a Binary Heap in C
We will implement a min heap in C as an example. The following is the code implementation:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
// Structure to represent a min heap
typedef struct {
int *arr;
int size;
int capacity;
} MinHeap;
// Function to create a new min heap
MinHeap* createMinHeap(int capacity) {
MinHeap* heap = (MinHeap*)malloc(sizeof(MinHeap));
heap->arr = (int*)malloc(capacity * sizeof(int));
heap->size = 0;
heap->capacity = capacity;
return heap;
}
// Function to get the index of the parent of a node
int parent(int i) {
return (i - 1) / 2;
}
// Function to get the index of the left child of a node
int leftChild(int i) {
return 2 * i + 1;
}
// Function to get the index of the right child of a node
int rightChild(int i) {
return 2 * i + 2;
}
// Function to swap two elements
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Function to heapify a subtree rooted at index i
void minHeapify(MinHeap* heap, int i) {
int left = leftChild(i);
int right = rightChild(i);
int smallest = i;
if (left < heap->size && heap->arr[left] < heap->arr[i]) {
smallest = left;
}
if (right < heap->size && heap->arr[right] < heap->arr[smallest]) {
smallest = right;
}
if (smallest != i) {
swap(&heap->arr[i], &heap->arr[smallest]);
minHeapify(heap, smallest);
}
}
// Function to insert a new element into the min heap
void insert(MinHeap* heap, int key) {
if (heap->size == heap->capacity) {
printf("Heap is full\n");
return;
}
heap->size++;
int i = heap->size - 1;
heap->arr[i] = key;
while (i != 0 && heap->arr[parent(i)] > heap->arr[i]) {
swap(&heap->arr[i], &heap->arr[parent(i)]);
i = parent(i);
}
}
// Function to extract the minimum element from the min heap
int extractMin(MinHeap* heap) {
if (heap->size <= 0) {
return -1;
}
if (heap->size == 1) {
heap->size--;
return heap->arr[0];
}
int root = heap->arr[0];
heap->arr[0] = heap->arr[heap->size - 1];
heap->size--;
minHeapify(heap, 0);
return root;
}
// Function to get the minimum element from the min heap
int getMin(MinHeap* heap) {
if (heap->size <= 0) {
return -1;
}
return heap->arr[0];
}
// Function to free the memory used by the min heap
void freeMinHeap(MinHeap* heap) {
free(heap->arr);
free(heap);
}
Usage Methods and Common Practices
Inserting Elements into the Heap
int main() {
MinHeap* heap = createMinHeap(MAX_SIZE);
insert(heap, 3);
insert(heap, 2);
insert(heap, 1);
printf("Minimum element: %d\n", getMin(heap));
freeMinHeap(heap);
return 0;
}
In this code, we first create a min heap with a maximum capacity of MAX_SIZE. Then we insert three elements into the heap and print the minimum element.
Extracting the Minimum Element
int main() {
MinHeap* heap = createMinHeap(MAX_SIZE);
insert(heap, 3);
insert(heap, 2);
insert(heap, 1);
int min = extractMin(heap);
printf("Extracted minimum element: %d\n", min);
printf("New minimum element: %d\n", getMin(heap));
freeMinHeap(heap);
return 0;
}
In this code, we extract the minimum element from the heap and print the extracted element and the new minimum element.
Best Practices
- Memory Management: Always free the memory allocated for the heap and its array to avoid memory leaks. In our implementation, we have a
freeMinHeapfunction to free the memory used by the min heap. - Error Handling: When inserting elements into the heap, check if the heap is full to avoid buffer overflow. Similarly, when extracting elements, check if the heap is empty to avoid accessing invalid memory.
- Code Readability: Use meaningful variable names and add comments to make the code more understandable.
Conclusion
In this blog post, we have introduced the fundamental concepts of heaps, implemented a min heap in C, and demonstrated how to use the heap for inserting and extracting elements. Heaps are a powerful data structure that can be used in many algorithms to improve efficiency. By following the best practices, you can ensure the reliability and performance of your heap implementation.
References
- Introduction to Algorithms, Third Edition by Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein.
- Data Structures and Algorithms in C, Second Edition by Adam Drozdek.