Implement a Binary Heap in JavaScript
Binary heaps are fundamental data structures used in various algorithms, including priority queues, heapsort, and graph algorithms like Dijkstra's. This challenge will guide you through implementing a binary heap, a key step towards mastering these advanced concepts.
Problem Description
Your task is to implement a Binary Heap data structure in JavaScript. A binary heap is a complete binary tree that satisfies the heap property. You should implement both a Min-Heap and a Max-Heap.
Key Requirements:
- Heap Property:
- Min-Heap: For any given node
i, the value of nodeiis less than or equal to the values of its children. The smallest element is always at the root. - Max-Heap: For any given node
i, the value of nodeiis greater than or equal to the values of its children. The largest element is always at the root.
- Min-Heap: For any given node
- Complete Binary Tree: All levels of the tree are fully filled except possibly the last level, which is filled from left to right. This property allows us to represent the heap efficiently using an array.
- Core Operations: Implement the following methods:
insert(value): Adds a new element to the heap while maintaining the heap property.extractRoot(): Removes and returns the root element (the minimum for Min-Heap, maximum for Max-Heap), then reorganizes the heap to maintain the heap property.peek(): Returns the root element without removing it.size(): Returns the number of elements in the heap.isEmpty(): Returnstrueif the heap is empty,falseotherwise.
Expected Behavior:
- The heap should correctly maintain its property after insertions and extractions.
- The
extractRoot()operation should always return the correct extreme element (min or max). - The heap should be able to handle duplicate values.
Edge Cases to Consider:
- Inserting into an empty heap.
- Extracting from an empty heap (should return
nullorundefined). - Extracting the last element.
- Heaps with only one element.
Examples
Example 1: Min-Heap insert and peek
Input:
const minHeap = new MinHeap();
minHeap.insert(3);
minHeap.insert(1);
minHeap.insert(4);
minHeap.insert(1); // Duplicate
minHeap.insert(5);
minHeap.insert(9);
minHeap.insert(2);
Output:
minHeap.peek() // Should return 1
minHeap.size() // Should return 7
Explanation: After inserting the elements, the Min-Heap arranges them such that the smallest element is at the root. The order in the internal array representation will vary depending on the insertion order, but the root will always be the minimum value.
Example 2: Min-Heap extractRoot
Input:
// Continuing from Example 1
const minHeap = new MinHeap();
minHeap.insert(3);
minHeap.insert(1);
minHeap.insert(4);
minHeap.insert(1);
minHeap.insert(5);
minHeap.insert(9);
minHeap.insert(2);
const firstExtract = minHeap.extractRoot(); // Should return 1
const secondExtract = minHeap.extractRoot(); // Should return 1
const thirdExtract = minHeap.extractRoot(); // Should return 2
Output:
firstExtract // 1
secondExtract // 1
thirdExtract // 2
minHeap.peek() // Should return 3
minHeap.size() // Should return 4
Explanation:
Each extractRoot() call removes the smallest element and re-heapifies. The duplicate '1's are removed first, followed by '2'.
Example 3: Max-Heap insert and extractRoot
Input:
const maxHeap = new MaxHeap();
maxHeap.insert(10);
maxHeap.insert(5);
maxHeap.insert(15);
maxHeap.insert(20);
maxHeap.insert(8);
const extractedMax1 = maxHeap.extractRoot(); // Should return 20
const extractedMax2 = maxHeap.extractRoot(); // Should return 15
Output:
extractedMax1 // 20
extractedMax2 // 15
maxHeap.peek() // Should return 10
maxHeap.size() // Should return 3
Explanation:
The Max-Heap ensures the largest element is always at the root. extractRoot() removes the largest and re-organizes.
Example 4: Edge Case - Empty Heap
Input:
const minHeap = new MinHeap();
minHeap.extractRoot(); // Should return undefined or null
minHeap.peek(); // Should return undefined or null
minHeap.isEmpty(); // Should return true
Explanation: Operations on an empty heap should be handled gracefully.
Constraints
- The heap will store numbers.
- The values of numbers will be within the standard JavaScript number range.
- The number of elements in the heap will not exceed 1,000,000.
- The time complexity for
insertandextractRootoperations should ideally be O(log n), where n is the number of elements in the heap. - The time complexity for
peek,size, andisEmptyoperations should be O(1).
Notes
- You can represent the binary heap using an array. The children of an element at index
iare typically at indices2*i + 1(left child) and2*i + 2(right child). The parent of an element at indexiis at indexMath.floor((i - 1) / 2). - When implementing
insert, you'll need to "bubble up" the new element to its correct position. - When implementing
extractRoot, you'll typically replace the root with the last element, remove the last element, and then "bubble down" the new root to its correct position. - Consider creating helper methods for
swap,bubbleUp, andbubbleDownto keep your code clean. - You will need to implement two separate classes,
MinHeapandMaxHeap.