hardHeapPattern: Heap
Implement Max Priority Queue Solution
Problem Statement
Implement a `MaxPriorityQueue` class that supports the following operations for integer values: 1. `push(value)`: Inserts the given `value` into the priority queue. 2. `pop()`: Removes and returns the element with the highest priority (maximum value). If the priority queue is empty, it should return `-1`. 3. `peek()`: Returns the element with the highest priority without removing it. If the priority queue is empty, it should return `-1`. The internal data structure should efficiently maintain the max-heap property.
Examples
Example 1:
Input:Operations: `[["push", 10], ["push", 5], ["peek"], ["pop"], ["push", 20], ["peek"], ["pop"]]`
Output:`[null, null, 10, 10, null, 20, 20]`
Explanation: Initially, 10 and 5 are pushed. `peek` returns 10. `pop` removes 10. Then 20 is pushed. `peek` returns 20, and `pop` removes 20.
Example 2:
Input:Operations: `[["pop"], ["peek"], ["push", 7], ["peek"], ["pop"], ["pop"]]`
Output:`[-1, -1, null, 7, 7, -1]`
Explanation: Initial `pop` and `peek` on an empty queue return -1. After pushing 7, `peek` returns 7, and `pop` removes it. The final `pop` on an empty queue returns -1.
Constraints
- `1 <= N <= 10^5` (N is the total number of operations)
- `-10^9 <= value <= 10^9` (for `push` operations)
- The `MaxPriorityQueue` will only contain integer values.
- Operations `pop()` and `peek()` on an empty priority queue must return `-1`.
Time: O(logN) for push and pop, O(1) for peek Space: O(N)
The optimal approach uses a binary max-heap, typically implemented using an array or ArrayList. This structure efficiently maintains the max-heap property, ensuring that the maximum element is always at the root. Both `push` (inserting and then 'heapifying up') and `pop` (removing the root, replacing with the last element, and then 'heapifying down') operations take O(logN) time, while `peek` is O(1).
Run, Test & Submit Code
Ready to practice this challenge? Launch our interactive compilation environment with compiler validation.
Solve on Interactive WorkspaceTested Solutions
import sys
class MaxPriorityQueue:
def __init__(self):
self.heap = []
# Helper functions to get indices of parent and children
def _getParentIndex(self, i):
return (i - 1) // 2
def _getLeftChildIndex(self, i):
return 2 * i + 1
def _getRightChildIndex(self, i):
return 2 * i + 2
# Helper functions to check existence of parent and children
def _hasParent(self, i):
return self._getParentIndex(i) >= 0
def _hasLeftChild(self, i):
return self._getLeftChildIndex(i) < len(self.heap)
def _hasRightChild(self, i):
return self._getRightChildIndex(i) < len(self.heap)
# Helper functions to get parent and children values
def _getParent(self, i):
return self.heap[self._getParentIndex(i)]
def _getLeftChild(self, i):
return self.heap[self._getLeftChildIndex(i)]
def _getRightChild(self, i):
return self.heap[self._getRightChildIndex(i)]
# Helper function to swap elements
def _swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
# Restores max-heap property by bubbling up the last element
def _heapifyUp(self):
index = len(self.heap) - 1
while self._hasParent(index) and self._getParent(index) < self.heap[index]:
self._swap(self._getParentIndex(index), index)
index = self._getParentIndex(index)
# Restores max-heap property by bubbling down the root element
def _heapifyDown(self):
index = 0
while self._hasLeftChild(index):
larger_child_index = self._getLeftChildIndex(index)
# If right child exists and is larger, consider it
if self._hasRightChild(index) and self._getRightChild(index) > self._getLeftChild(index):
larger_child_index = self._getRightChildIndex(index)
# If current element is already greater than or equal to its largest child,
# max-heap property is satisfied.
if self.heap[index] >= self.heap[larger_child_index]:
break
else:
# Swap with the larger child and continue heapifying down
self._swap(index, larger_child_index)
index = larger_child_index
""""""
Inserts the given value into the priority queue.
Time Complexity: O(log N) due to heapifyUp.
:param value: The integer value to insert.
""""""
def push(self, value):
self.heap.append(value)
self._heapifyUp()
""""""
Removes and returns the element with the highest priority (maximum value).
If the priority queue is empty, it returns -1.
Time Complexity: O(log N) due to heapifyDown.
:return: The maximum value or -1 if empty.
""""""
def pop(self):
if len(self.heap) == 0:
return -1
if len(self.heap) == 1:
return self.heap.pop()
item = self.heap[0] # The max element is always at the root
self.heap[0] = self.heap.pop() # Move the last element to the root
self._heapifyDown() # Restore heap property
return item
""""""
Returns the element with the highest priority without removing it.
If the priority queue is empty, it returns -1.
Time Complexity: O(1).
:return: The maximum value or -1 if empty.
""""""
def peek(self):
if len(self.heap) == 0:
return -1
return self.heap[0] # The max element is always at the root
# Driver code for reading from stdin and printing to stdout
pq = MaxPriorityQueue()
output = []
for line in sys.stdin:
parts = line.strip().split()
command = parts[0]
if command == 'push':
value = int(parts[1])
pq.push(value)
elif command == 'pop':
output.append(str(pq.pop()))
elif command == 'peek':
output.append(str(pq.peek()))
sys.stdout.write("\n".join(output))
sys.stdout.write("\n") # Ensure a trailing newline